A股上市公司传智教育(股票代码 003032)旗下技术交流社区北京昌平校区

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<Device> data;    public Meta getMeta() {        return meta;    }    public void setMeta(Meta meta) {        this.meta = meta;    }    public List<Device> getData() {        return data;    }    public void setData(List<Device> 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<T>{    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<String>();    myClass1.setData("Cyy");    System.out.println(myClass1.getData());    MyClass1 myClass2 = new MyClass1<Integer>();    myClass2.setData(10);    System.out.println(myClass2.getData());复制代码输出:
    Cyy10复制代码有没有发现,我们不用进行强制类型转换仍然能输出正确的数值。注意下,当我们new MyClass1<String>()传的是String那么我们类里面的所有T就都是String类型了。
    总结:
    泛型类使用优点:

    • 防止类膨胀
    • 不再手动进行类型转换

    泛型类的使用
    • 泛型的类型参数可以是泛型类
    static class MyClass1<T1>    {        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<MyClass1<Student>> myClass1MyClass1 = new  MyClass1<MyClass1<Student>>();        MyClass1<Student> myClass1 = new MyClass1<Student>();        myClass1.setData1(new Student("cyy"));        myClass1MyClass1.setData1(myClass1);        System.out.println(myClass1MyClass1.getData1().getData1().toString());复制代码输出:
    Student{name='cyy'}复制代码
    • 泛型类可以同时设置多个类型参数
    static class MyClass1<T1,T2>    {        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<String,Integer> myClass1 = new MyClass1<String,Integer>();        myClass1.setData1("Cyy");        myClass1.setData2(25);        System.out.println(myClass1.getData1());        System.out.println(myClass1.getData2());复制代码输出:
    Cyy25复制代码
    • 泛型类可以继承泛型类
    class SuperClass<T1>    {        private T1 var1;        public SuperClass(T1 var1) {            this.var1 = var1;        }        public T1 show1()        {            return var1;        }    }    class SubClass<T1,T2> extends SuperClass<T1>    {        private T2 var2;        public SubClass(T1 var1, T2 var2) {            super(var1);            this.var2 = var2;        }        @Override        public T1 show1() {            return super.show1();        }    }复制代码使用:
        SubClass<String,Integer> subClass = new SubClass<>("cyy",25);    System.out.println(subClass.show1());复制代码输出:
    cyy复制代码
    • 泛型类可以实现泛型接口
    interface IInfo<T2>    {        public void show2(T2 var3);    }  static class SubClass<T1,T2> extends SuperClass<T1> implements IInfo<T2>    {        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<String,Integer> subClass = new SubClass<>("cyy",25);        subClass.show2(100);        System.out.println(subClass.show1());复制代码输出:
    10025cyy复制代码注:不可以进行泛型变量之间的运算,因为泛型变量在编译期间会进行类型擦除,全部变成Object,比如Object+Object就不知道是什么类型了,所以这点很重要。
    OK,现在我们可以回到最初那个问题上了,我们可以利用泛型定义一个CommResult类。
    public class CommResult <T> {    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<T extends Animal>    {        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<Dog> genericClass = new GenericClass<>(new Dog());        genericClass.getData().eat();        GenericClass<Cat> genericClasscat = new GenericClass<>(new Cat());        genericClasscat.getData().eat();复制代码如果换成接口呢?
    class GenericClass<T implements eat>{    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<T extends eat>{    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<Boolean> f1 = new Generic<Booleab>();Generic<integer> f2 = new Generic<integer>();f1 = f2;//发生编译错误Generic<Object> f = f1 ;//f1和f类型并不兼容,发生编译错误f = f2;//f2和f类型同样不兼容,也会发生编译错误。复制代码泛型类实例之间的不兼容性会带来使用的不便。我们可以使用泛型通配符(?)生命泛型类的变量就可以解决这个问题。
    泛型通配的使用方式
    • "?" 代表一个类型。
    Generic<Boolean> f1 = new Generic<Booleab>();Generic<?> f= f1;复制代码
    • 和限制泛型的上线相似,同样可以使用extends关键字限定通配符匹配类型的上线:
    Generic<Dog> f1 = new Generic<Dog>();Generic<? extends Animal> f= f1;复制代码
    • 还可以使用super关键词将通配符匹配类型限定为某个类型及其父类型
    Generic<Animal> f1 = new Generic<Animal>();Generic<? super Dog> f= f1;复制代码现在要在这里特别说下两个限定通配符
    • extends 上边界限定通配符
    举个例子一看就懂了,<? extends Animal> , 那这里的`?`就必须是Animal的子类或它自己。复制代码
    • super 下边界限定通配符
    举个例子一看就懂了,<? super Dog> , 那这里的`?`就必须是Dog的父类或它自己。复制代码5.泛型方法使用不仅类可以声明泛型,类中的方法也可以声明仅用于自身的泛型,这种方法叫做泛型方法。其定义格式为:
    访问修饰符<泛型列表> 返回类型 方法名(参数列表) {    实现代码}复制代码在泛型列表中声明的泛型,可用于该方法的返回类型声明,参数类型声明和方法代码中的局部变量的类型声明。
    类中其他方法不能使用当前方法声明的泛型。
    注:是否拥有泛型方法,与其所在的类是否是泛型没有关系。要定义泛型方法,秩序将泛型参数列表置于返回值之前。
    什么时候使用泛型方法,而不是泛型类呢?
    • 添加类型约束只作用于一个方法的多个参数之间,而不涉及类中的其他方法时。
    • 施加类型约束的方法为静态方法,只能将其定义为泛型方法,因为静态方法不能使用其所在类的类型参数。

    再举个代码的例子:
    现在我们先定义一个泛型类:
    public class Demo1 {   public static void main(String[] args)   {        GenericClassOne<String> genericClassOne = new GenericClassOne<>();        genericClassOne.printlinT(10);    }}class GenericClassOne<T>{    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<T>{    //泛型方法,类型定义写在返回值之前了    public <T> void printlinT(T content)    {        System.out.println(content);    }}复制代码这下不会再报编译错误了,现在看下打印结果。
    输出:
    10cyy12.5复制代码这样是不是就灵活了许多啦~
    那么泛型的方法可不可以重载呀,当然可以,我们仍然可以写成这样。
    class GenericClassOne<T>{    //泛型方法,类型定义写在返回值之前了    public <T> void printlinT(T content)    {        System.out.println(content);    }    //泛型方法,类型定义写在返回值之前了    public <T extends Animal> void printlinT(T animal)    {        animal.eat();    }}abstract class Animal{    public abstract void eat();}复制代码因为泛型类在编译过程中会有个擦除的工作,所以第一个printlnT(T content)中的泛型会变成object,而第二个泛型方法中的T会变成Animal。所以他的方法可以被重载。
    Ok,结束!


【转载】
作者:正儿八经小青年就是我
链接:https://juejin.im/post/5bdfb0a251882517165d7837



3 个回复

正序浏览
回复 使用道具 举报
~(。≧3≦)ノ⌒☆
回复 使用道具 举报
您需要登录后才可以回帖 登录 | 加入黑马