博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
Java泛型理解与使用
阅读量:5945 次
发布时间:2019-06-19

本文共 15983 字,大约阅读时间需要 53 分钟。

1.泛型简介

  • 问题:在获取用户信息的API中,后台给我们返回一个这样形式的json字符串。

    {    "meta": {        "code": 0,        "message": "ok"    },    "data": {        "nick_name": "hellokitty",        "cellphone": "18301824843",    }}复制代码

    我们用fastJson解析上述json字符串时候,该怎么处理? ,我们是不是就会写这样一个类。

    public class User {    private Meta meta;    private Data data;    public Meta getMeta() {        return meta;    }    public void setMeta(Meta meta) {        this.meta = meta;    }    public Data getData() {        return data;    }    public void setData(Data data) {        this.data = data;    }    static class Meta    {        private String code;        private String message;        public String getCode() {            return code;        }        public void setCode(String code) {            this.code = code;        }        public String getMessage() {            return message;        }        public void setMessage(String message) {            this.message = message;        }    }    static class Data    {        private String nick_name;        private String cellphone;        public String getNick_name() {            return nick_name;        }        public void setNick_name(String nick_name) {            this.nick_name = nick_name;        }        public String getCellphone() {            return cellphone;        }        public void setCellphone(String cellphone) {            this.cellphone = cellphone;        }    }}复制代码

    然后调用fastjason的JSON.parseObject(msg,User.class)进行解析。

    而如果拉取设备列表API返回的数据格式是这样的一个形式,我们该怎么处理?

    {    "meta": {        "code": 0,        "message": "ok"    },    "data": [        {            "device_id": "4acb634aaf5711e8b290000c29c27f42",            "role": 1,            "device_alias": "hellokitty",            "created_at": "2018-09-04T10:55:57"        },        {            "device_id": "4acb634aaf5711e8b290000c29c27f42",            "role": 1,            "device_alias": "hellokitty",            "created_at": "2018-09-04T10:55:57"        }    ]}复制代码

    是不是我们仍然要再写一个解析类来解析这个设备列表类。

    public class DeviceList {    private Meta meta;    private List data;    public Meta getMeta() {        return meta;    }    public void setMeta(Meta meta) {        this.meta = meta;    }    public List getData() {        return data;    }    public void setData(List data) {        this.data = data;    }    static class Meta    {        private String code;        private String message;        public String getCode() {            return code;        }        public void setCode(String code) {            this.code = code;        }        public String getMessage() {            return message;        }        public void setMessage(String message) {            this.message = message;        }    }    static class Device    {        @Override        public String toString() {            return "Device{" +                    "device_id='" + device_id + '\'' +                    ", role=" + role +                    ", device_alias='" + device_alias + '\'' +                    ", created_at='" + created_at + '\'' +                    '}';        }        private String device_id;        private int role;        private String device_alias;        private String created_at;        public String getDevice_id() {            return device_id;        }        public void setDevice_id(String device_id) {            this.device_id = device_id;        }        public int getRole() {            return role;        }        public void setRole(int role) {            this.role = role;        }        public String getDevice_alias() {            return device_alias;        }        public void setDevice_alias(String device_alias) {            this.device_alias = device_alias;        }        public String getCreated_at() {            return created_at;        }        public void setCreated_at(String created_at) {            this.created_at = created_at;        }    }}复制代码

    如果每次都这样的话,会不会要创建很多很相像的类,他们只是里面部分变量不同,其他的部分都相同。

    再举一个栗子: 如果我们想要产生多个对象,每个对象的逻辑完全一样,只是对象内的成员变量的类型不同,那我们如何去做? 在下面我们创建了两个类,只是data的变量类型不同,是不是也可以达到我们刚才的要求。

    static class MyClass1{    public MyClass1() {    }    private String data;    public MyClass1(String data) {        this.data = data;    }    public String getData() {        return data;    }    public void setData(String data) {        this.data = data;    }}static class MyClass2{    public MyClass2() {    }    private int data;    public MyClass2(int data) {        this.data = data;    }    public int getData() {        return data;    }    public void setData(int data) {        this.data = data;    }}复制代码

    打印结果:

    MyClass1 myClass1 = new MyClass1();    myClass1.setData("Cyy");    MyClass2 myClass2 = new MyClass2();    myClass2.setData(10);    System.out.println(myClass1.getData());    System.out.println(myClass2.getData());复制代码

    输出:

    Cyy10复制代码

    但是如果我们还想要这样一个对象呢,那我们是不是还要继续去创建这样的对象,那如果我还要10个这个的对象呢,那我们是不是就要创建十个。这样明显是很笨重的一种解决方案。

    那我们现在思考,如果我们用Object来代替呢?

    static class MyClass1    {        public MyClass1() {        }        private Object data;        public MyClass1(Object data) {            this.data = data;        }        public Object getData() {            return data;        }        public void setData(Object data) {            this.data = data;        }    }复制代码

    打印输出:

    MyClass1 myClass1 = new MyClass1();    myClass1.setData("Cyy");    System.out.println((String)myClass1.getData());    MyClass1 myClass2 = new MyClass1();    myClass2.setData(10);    System.out.println((int)myClass2.getData());    ```输出结果:复制代码
    Cyy  10复制代码
    呀~看上去好像完美解决了,不用创建多个类,就可以实现刚才需要功能,好像很完美,现在让他变成不完美,现在我们让他这样打印出来.复制代码
    MyClass1 myClass2 = new MyClass1();      myClass2.setData(10);      System.out.println((String)myClass2.getData());复制代码
    注意我们给他的是整型,但是打印时候我们给他的强转类型是String,现在看下会发生什么问题。复制代码
    Exception in thread "main" java.lang.ClassCastException: java.lang.Integer cannot be cast to java.lang.String  at SecDemo.main(SecDemo.java:13)复制代码
    它提示了,类型转换异常。 总结            方案(一) :            方法: 创建多个类文件,给每个类中的成员变量设置指定的数据类型。            缺点: 导致类的膨胀,重用性太差            方案(二) :            方法: 创建一个类文件,给这个类中的成员变量设置Object数据类型            缺点:编译的时候正常,但运行时候可能会报错.            泛型类就能很好的解决以上两个问题。复制代码

2.泛型类

  • 泛型是JDK1.5引入的新特性,也是最重要的一个特性。

  • 泛型可以在编译的时候检查类型安全,并且所有的强制转换都是自动和隐式的。

  • 泛型的原理就是类型的参数化,即把类型看做参数,也就是说把所要操作的数据类型看做参数,就像方法的形式参数是运行时传递的值一样。

  • 简单的说,类型变量扮演的角色如同一个参数,它提供给编译器用来类型检查的信息。

  • 泛型可以提高代码的扩展性和重用性 **

    如果我们将刚才的类改成泛型类是什么样子的呢?

    static class MyClass1
    { public MyClass1() { } private T data; public MyClass1(T data) { this.data = data; } public T getData() { return data; } public void setData(T data) { this.data = data; }}复制代码

    我们发现在类的开头多了个,这个就代表着传入进来的参数,他可以是整型,可以是字符串类型,只要你传进来了那么后续的get,set方法就全部都是这种类型了。他就相当于一个操作的参数。好的现在我们试一下。

    打印输出:

    MyClass1 myClass1 = new MyClass1
    (); myClass1.setData("Cyy"); System.out.println(myClass1.getData()); MyClass1 myClass2 = new MyClass1
    (); myClass2.setData(10); System.out.println(myClass2.getData());复制代码

    输出:

    Cyy10复制代码

    有没有发现,我们不用进行强制类型转换仍然能输出正确的数值。 注意下,当我们new MyClass1<String>()传的是String那么我们类里面的所有T就都是String类型了。

    总结:

    泛型类使用优点:

    • 防止类膨胀

    • 不再手动进行类型转换

    泛型类的使用
    1. 泛型的类型参数可以是泛型类
    static class MyClass1
    { public MyClass1() { } private T1 data1; public T1 getData1() { return data1; } public void setData1(T1 data1) { this.data1 = data1; } } static class Student { private String name; public Student(String name) { this.name = name; } @Override public String toString() { return "Student{" + "name='" + name + '\'' + '}'; } public String getName() { return name; } public void setName(String name) { this.name = name; } } 复制代码

    使用:

    MyClass1
    > myClass1MyClass1 = new MyClass1
    >(); MyClass1
    myClass1 = new MyClass1
    (); myClass1.setData1(new Student("cyy")); myClass1MyClass1.setData1(myClass1); System.out.println(myClass1MyClass1.getData1().getData1().toString());复制代码

    输出:

    Student{name='cyy'}复制代码
    1. 泛型类可以同时设置多个类型参数
    static class MyClass1
    { public MyClass1() { } private T1 data1; private T2 data2; public T2 getData2() { return data2; } public void setData2(T2 data2) { this.data2 = data2; } public T1 getData1() { return data1; } public void setData1(T1 data1) { this.data1 = data1; } }复制代码

    使用:

    MyClass1
    myClass1 = new MyClass1
    (); myClass1.setData1("Cyy"); myClass1.setData2(25); System.out.println(myClass1.getData1()); System.out.println(myClass1.getData2());复制代码

    输出:

    Cyy25复制代码
    1. 泛型类可以继承泛型类
    class SuperClass
    { private T1 var1; public SuperClass(T1 var1) { this.var1 = var1; } public T1 show1() { return var1; } } class SubClass
    extends SuperClass
    { private T2 var2; public SubClass(T1 var1, T2 var2) { super(var1); this.var2 = var2; } @Override public T1 show1() { return super.show1(); } }复制代码

    使用:

    SubClass
    subClass = new SubClass<>("cyy",25); System.out.println(subClass.show1());复制代码

    输出:

    cyy复制代码
    1. 泛型类可以实现泛型接口
    interface IInfo
    { public void show2(T2 var3); } static class SubClass
    extends SuperClass
    implements IInfo
    { private T2 var2; public SubClass(T1 var1, T2 var2) { super(var1); this.var2 = var2; } @Override public T1 show1() { return super.show1(); } @Override public void show2(T2 var3) { System.out.println(var3); System.out.println(var2); } }复制代码

    使用:

    SubClass
    subClass = new SubClass<>("cyy",25); subClass.show2(100); System.out.println(subClass.show1());复制代码

    输出:

    10025cyy复制代码

    注:不可以进行泛型变量之间的运算,因为泛型变量在编译期间会进行类型擦除,全部变成Object,比如Object+Object就不知道是什么类型了,所以这点很重要。

    OK,现在我们可以回到最初那个问题上了,我们可以利用泛型定义一个CommResult类。

    public class CommResult 
    { private Meta meta; private T data; public Meta getMeta() { return meta; } public void setMeta(Meta meta) { this.meta = meta; } public T getData() { return data; } public void setData(T data) { this.data = data; } static class Meta { private String code; private String message; public String getCode() { return code; } public void setCode(String code) { this.code = code; } public String getMessage() { return message; } public void setMessage(String message) { this.message = message; } }}复制代码

    然后使用的时候我们可以这样:

    JSON.parseObject(msg,CommResult<User>)JSON.parseObject(msg,CommResult<List<Device>>)

    这样就完美避免了创建多个结构一样,但是只有里面部分变量不一致的类了。

    3.限制泛型可用类型

    在定义泛型类别时,默认在实例化泛型类的时候可以使用任何类型,但是如果想要限制使用泛型时,只能用某个特定类型或者是其子类型才能实例化该类型时,可以在定义类型时,使用extends关键字指定这个类型必须是继承某个类,或者实现某个接口。 当没有指定泛型继承的类型或接口时,默认使用extends Object,所以默认情况下,可以使用任何类型作为参数。

    class GenericClass
    { private T data; public GenericClass(T data) { this.data = data; } public T getData() { return data; } public void setData(T data) { this.data = data; } } abstract class Animal { public abstract void eat(); } class Dog extends Animal { @Override public void eat() { System.out.println("啃骨头"); } } class Cat extends Animal { @Override public void eat() { System.out.println("吃鱼肉"); } }复制代码

    现在我们看下,如果我在泛型类里面传个String类型的参数,看他会报什么? Type parameter 'java.lang.String' is not within its bound; should extend 'Test.Animal

    他说String不是Animal子类,不行吧。

    如果我们换成这样就可以了。

    GenericClass
    genericClass = new GenericClass<>(new Dog()); genericClass.getData().eat(); GenericClass
    genericClasscat = new GenericClass<>(new Cat()); genericClasscat.getData().eat();复制代码

    如果换成接口呢?

    class GenericClass
    { private T data; public GenericClass(T data) { this.data = data; } public T getData() { return data; } public void setData(T data) { this.data = data; }}复制代码

    这样写对不对,这样写是不对的,编译器会报错的,因为不管是接口还是类,都要用extends。所以换成接口也要写成这样就可以了。

    class Cat implements eat{    @Override    public void eat() {        System.out.println("吃鱼肉");    }}class Dog implements eat{    @Override    public void eat() {        System.out.println("啃骨头");    }}interface eat{    public abstract void eat();}class GenericClass
    { private T data; public GenericClass(T data) { this.data = data; } public T getData() { return data; } public void setData(T data) { this.data = data; }}复制代码

    4.类型通配声明

    同一泛型类,如果实例化时给定的实际类型不同,则这些实例的类型是不兼容的,不能相互赋值。如:

    Generic
    f1 = new Generic
    ();Generic
    f2 = new Generic
    ();f1 = f2;//发生编译错误Generic
    f = f1 ;//f1和f类型并不兼容,发生编译错误f = f2;//f2和f类型同样不兼容,也会发生编译错误。复制代码

    泛型类实例之间的不兼容性会带来使用的不便。我们可以使用泛型通配符(?)生命泛型类的变量就可以解决这个问题。

    泛型通配的使用方式

    • "?" 代表一个类型。
    Generic
    f1 = new Generic
    ();Generic
    f= f1;复制代码
    • 和限制泛型的上线相似,同样可以使用extends关键字限定通配符匹配类型的上线:
    Generic
    f1 = new Generic
    ();Generic
    f= f1;复制代码
    • 还可以使用super关键词将通配符匹配类型限定为某个类型及其父类型
    Generic
    f1 = new Generic
    ();Generic
    f= f1;复制代码

    现在要在这里特别说下两个限定通配符

    • extends 上边界限定通配符
    举个例子一看就懂了,
    , 那这里的`?`就必须是Animal的子类或它自己。复制代码
    • super 下边界限定通配符
    举个例子一看就懂了,
    , 那这里的`?`就必须是Dog的父类或它自己。复制代码

    5.泛型方法使用

    不仅类可以声明泛型,类中的方法也可以声明仅用于自身的泛型,这种方法叫做泛型方法。其定义格式为:

    访问修饰符
    <泛型列表>
    返回类型 方法名(参数列表) { 实现代码}复制代码

    在泛型列表中声明的泛型,可用于该方法的返回类型声明,参数类型声明和方法代码中的局部变量的类型声明。

    类中其他方法不能使用当前方法声明的泛型。

    注:是否拥有泛型方法,与其所在的类是否是泛型没有关系。要定义泛型方法,秩序将泛型参数列表置于返回值之前。

    什么时候使用泛型方法,而不是泛型类呢?

    • 添加类型约束只作用于一个方法的多个参数之间,而不涉及类中的其他方法时。

    • 施加类型约束的方法为静态方法,只能将其定义为泛型方法,因为静态方法不能使用其所在类的类型参数。

    再举个代码的例子:

    现在我们先定义一个泛型类:

    public class Demo1 {   public static void main(String[] args)   {        GenericClassOne
    genericClassOne = new GenericClassOne<>(); genericClassOne.printlinT(10); }}class GenericClassOne
    { public void printlinT(T content) { System.out.println(content); }}复制代码

    如果我们这么写,肯定编译就报错误了吧,因为我们上面定义的是String类型,但是我们传给他的是int型的。那如果这样的话,这个方法是不是就有局限性了。

    那如果我们现在使用泛型方法呢?该怎么写?

    public class Demo1 {    public static void main(String[] args)    {        GenericClassOne genericClassOne = new GenericClassOne();        genericClassOne.printlinT(10);        genericClassOne.printlinT("cyy");        genericClassOne.printlinT(12.5);    }}class GenericClassOne
    { //泛型方法,类型定义写在返回值之前了 public
    void printlinT(T content) { System.out.println(content); }}复制代码

    这下不会再报编译错误了,现在看下打印结果。

    输出:

    10cyy12.5复制代码

    这样是不是就灵活了许多啦~

    那么泛型的方法可不可以重载呀,当然可以,我们仍然可以写成这样。

    class GenericClassOne
    { //泛型方法,类型定义写在返回值之前了 public
    void printlinT(T content) { System.out.println(content); } //泛型方法,类型定义写在返回值之前了 public
    void printlinT(T animal) { animal.eat(); }}abstract class Animal{ public abstract void eat();}复制代码

    因为泛型类在编译过程中会有个擦除的工作,所以第一个printlnT(T content)中的泛型会变成object,而第二个泛型方法中的T会变成Animal。所以他的方法可以被重载。

    Ok,结束!

转载地址:http://szzxx.baihongyu.com/

你可能感兴趣的文章
对网页是否为当前展示标签页、是否最小化、以及是否后台运行进行监听
查看>>
听君一席话,胜读十年书
查看>>
2.pandas数据清洗
查看>>
base64转码java版
查看>>
人工智能AI-机器视觉CV-数据挖掘DM-机器学习ML-神经网络-[资料集合贴]
查看>>
秋季4类疾病患者忌吃螃蟹
查看>>
POJ 1328 Radar lnstallation 2
查看>>
jquery鼠标悬停突出显示
查看>>
iOS enum 定义与使用
查看>>
EXPLAIN PLAN获取SQL语句执行计划
查看>>
20189218 2018-2019-2 《密码与安全新技术专题》第9周作业
查看>>
Mybatis操作数据库实现主键自增长
查看>>
NHibernate初学二之简单执行SQL及HQL、Linq
查看>>
充电电池和充电时间说明
查看>>
输出昨天的当前时刻
查看>>
字段为NULL导致MyBatis在Oracle上执行SQL报错,无效的列类型
查看>>
知识点2-对二进制的运用
查看>>
PAT 1102 Invert a Binary Tree[比较简单]
查看>>
理解 JavaScript 作用域和作用域链
查看>>
Beta冲刺提交-星期四
查看>>