1. Introduction to generics

  • Problem: In the API for retrieving user information, the background returns us a JSON string of this form.

    {
        "meta": {
            "code": 0."message": "ok"
        },
        "data": {
            "nick_name": "hellokitty"."cellphone": "18301824843",}}Copy the code

    What happens when we parse the json string with fastJson? , we would write a class like this.

    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; }}}Copy the code

    Then call fastJason’s json.parseObject (MSG, user.class) to parse.

    What if the pull device list API returns data in such a format?

    {
        "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"}}]Copy the code

    Do we still have to write another parse class to parse the device list class?

        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; }}}Copy the code

    If this were the case every time, wouldn’t you have to create a lot of classes that are very similar, with only some of the variables different, but other parts the same?

    Here’s another example: If we want to produce multiple objects, each of which has exactly the same logic but different types of member variables, how do we do this? In the following we create two classes, but data variable type is different, whether can also meet the requirements we just.

     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; }}Copy the code

    Print result:

        MyClass1 myClass1 = new MyClass1();
        myClass1.setData("Cyy");
        MyClass2 myClass2 = new MyClass2();
        myClass2.setData(10);
        System.out.println(myClass1.getData());
        System.out.println(myClass2.getData());
    Copy the code

    Output:

    Cyy
    10
    Copy the code

    But if we still want this object, are we going to go ahead and create this object, and if I still want 10 objects of this object, are we going to create 10 objects of this object. This is obviously an unwieldy solution.

    So now we’re thinking, what if we use Object instead?

        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; }}Copy the code

    Printout:

       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()); Output result:Copy the code
      Cyy
      10
    Copy the code
    Ah, it looks like it's working out perfectly, you don't have to create multiple classes to do what you just did, it looks perfect, now let's make it imperfect, now let's make it print like this.Copy the code
       MyClass1 myClass2 = new MyClass1();
          myClass2.setData(10);
          System.out.println((String)myClass2.getData());
    Copy the code
    Notice we gave it an integer, but we gave it a strong String when we printed it, so let's see what happens.Copy the code
      Exception in thread "main" java.lang.ClassCastException: java.lang.Integer cannot be cast to java.lang.String
      at SecDemo.main(SecDemo.java:13)
    Copy the code
    It tells you that there is a type conversion exception. Summary scheme (A) : method: create multiple class files, set the specified data type for the member variables in each class. Disadvantages: Expansion of the class, poor reuse scheme (2) : method: create a class file, set the Object data type to the member variables in this class Disadvantages: the compilation is fine, but the runtime may report an error. Generic classes solve both of these problems well.Copy the code

2. A generic class

  • Generics are a new feature introduced in JDK1.5, and one of the most important.

  • Generics can be checked for type safety at compile time, and all casts are automatic and implicit.

  • The principle behind generics is the parameterization of types, that is, treating types as parameters, that is, treating the data types to be manipulated as parameters, just as the formal parameters of methods are values passed at run time.

  • Simply put, a type variable plays the role of a parameter that provides information to the compiler for type checking.

  • Generics make your code more extensible and reusable

    What if we changed that class to a generic class?

    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; }}Copy the code

    We notice that there’s an extra one at the beginning of the class, and that’s the argument that’s being passed in, it can be an integer, it can be a string, and once you pass it in then all the subsequent get and set methods are going to be of that type. It is equivalent to an operation parameter. Ok, now let’s try it out.

    Printout:

    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());
    Copy the code

    Output:

    Cyy
    10
    
    Copy the code

    Notice that we can still print the correct value without casting it. Note that when we pass new MyClass1

    () as a String, all T’s in our class are strings.

    Conclusion:

    Advantages of using generic classes:

    • Anti-inflation

    • No more manual type conversion

    Use of generic classes
    1. The type parameter of a generic type can be a generic class
    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; }}Copy the code

    Use:

     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());
    Copy the code

    Output:

    Student{name='cyy'}
    Copy the code
    1. Generic classes can set multiple type parameters at the same time
    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; }}Copy the code

    Use:

    MyClass1<String,Integer> myClass1 = new MyClass1<String,Integer>();
            myClass1.setData1("Cyy");
            myClass1.setData2(25);
            System.out.println(myClass1.getData1());
            System.out.println(myClass1.getData2());
    Copy the code

    Output:

    Cyy
    25
    Copy the code
    1. Generic classes can inherit from generic classes
    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() {
                returnsuper.show1(); }}Copy the code

    Use:

        SubClass<String,Integer> subClass = new SubClass<>("cyy", 25); System.out.println(subClass.show1());Copy the code

    Output:

    cyy
    
    Copy the code
    1. Generic classes can implement generic interfaces
     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() {
                returnsuper.show1(); } @Override public void show2(T2 var3) { System.out.println(var3); System.out.println(var2); }}Copy the code

    Use:

     SubClass<String,Integer> subClass = new SubClass<>("cyy", 25); subClass.show2(100); System.out.println(subClass.show1());Copy the code

    Output:

    100
    25
    cyy
    Copy the code

    Note: Operations between generic variables are not allowed because they are erased at compile time and all become Object. For example, Object+Object does not know what type they are. This is important.

    OK, now we can go back to the original problem and define a CommResult class using generics.

    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; }}}Copy the code

    Then we can use it like this:

    JSON. ParseObject (MSG, CommResult < User >) or JSON. ParseObject (MSG, CommResult < List < Device > >).

    This is the perfect way to avoid creating classes with multiple structures in which only some of the variables are inconsistent.

    3. Restrict the available types of generics

    In defining a generic category, the default on the instantiation of the generic class when you can use any type, but if you want to limit the use of generics, can only use a certain type or his son to instantiate the type, can be defined type, use the extends keyword specifies the type must be inherit a class, or implementing an interface. Extends Object is used by default when no type or interface is specified for generic inheritance, so by default, any type can be used as an argument.

     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("Bone gnawing");
            }
        }
    
        class Cat extends Animal
        {
    
            @Override
            public void eat() {
    
                System.out.println("Eat fish"); }}Copy the code

    Now let’s see, if I pass a String in a generic class, what does it say? Type parameter ‘java.lang.String’ is not within its bound; should extend ‘Test.Animal

    He said String is not a subclass of Animal.

    If we switch to this, we can do it.

    GenericClass<Dog> genericClass = new GenericClass<>(new Dog());
            genericClass.getData().eat();
            GenericClass<Cat> genericClasscat = new GenericClass<>(new Cat());
            genericClasscat.getData().eat();
    Copy the code

    What about interfaces?

    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; }}Copy the code

    Is this right? It’s not right. The compiler will give you an error because extends is used for both an interface and a class. So I’m going to write interfaces like this.

    class Cat implements eat
    {
    
        @Override
        public void eat() {
    
            System.out.println("Eat fish");
        }
    }
    
    class Dog implements eat
    {
    
        @Override
        public void eat() {
    
            System.out.println("Bone gnawing");
        }
    }
    
    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; }}Copy the code

    4. Type wildcard declaration

    If the same generic class is instantiated with different actual types, the types of these instances are incompatible and cannot be assigned to each other. Such as:

    Generic<Boolean> f1 = new Generic<Booleab>();
    Generic<integer> f2 = new Generic<integer> (); f1 = f2; // compiler error Generic<Object> f = f1; //f = f2; F2 and f types are also incompatible, and compilation errors can occur.Copy the code

    Incompatibilities between generic class instances can cause inconvenience. We can use generic wildcards (?). The variables of the life generic class solve this problem.

    How generic wildcards are used

    • “?” Represents a type.
    Generic<Boolean> f1 = new Generic<Booleab>(); Generic<? > f= f1;Copy the code
    • Similar to limiting the limits of generics, you can also limit the limits of wildcard matching types using the extends keyword:
    Generic<Dog> f1 = new Generic<Dog>();
    
    Generic<? extends Animal> f= f1;
    Copy the code
    • You can also use the super keyword to limit the wildcard matching type to a type and its parent type
    Generic<Animal> f1 = new Generic<Animal>();
    
    Generic<? super Dog> f= f1;
    Copy the code

    Now there are two qualified wildcards in particular

    • extendsThe upper boundary qualifies wildcards
    For example,<? Extends Animal> extends Animal> 'must be a subclass of Animal or itself.Copy the code
    • superThe lower boundary qualifies wildcards
    For example,<? Super Dog>, what's this'? 'must be Dog's parent or itself.Copy the code

    5. Use of generic methods

    Not only classes can declare generics, but methods in a class can also declare generics that apply only to themselves, called generic methods. Its definition format is:

    Access modifier < generic list > Return type method name (parameter list) {implementation code}Copy the code

    Generics declared in the generic list can be used for return type declarations of the method, parameter type declarations, and type declarations of local variables in the method code.

    Other methods in the class cannot use generics declared by the current method.

    Note: Having generic methods does not matter if the class is generic. To define generic methods, order precedes the list of generic parameters before the return value.

    When to use generic methods instead of generic classes?

    • Adding type constraints applies only to multiple parameters of a method and does not involve other methods in the class.

    • Methods that impose type constraints are static and can only be defined as generic because static methods cannot use the type parameters of their class.

    Here’s another example of code:

    Now let’s define a generic class:

    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); }}Copy the code

    If we did that, we would get an error because we defined a String, but we passed it an int. So if that’s the case, isn’t there a limitation to the method?

    What if we now use the generic approach? How do I write it?

        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); }}Copy the code

    No more compile errors, now look at the print.

    Output:

    10
    cyy
    12.5
    Copy the code

    So be flexible a lot of ~

    Can generic methods be overridden? Sure, we can still write it like this.

    Public <T> void printlinT(T content) {system.out.println (content); GenericClassOne<T> {// 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(); }Copy the code

    Because generic classes are erased during compilation, the generic in the first printlnT(T content) becomes Object, and the T in the second generic method becomes Animal. So his method can be overridden.

    Ok, the end!