First, we define four classes A, B, C, and D. Their relationship is as follows

class A {}
class B extends A {}
class C extends B {}
class D extends C {}
Copy the code

Generic types are not specified

// The following code is compiled
List list = new ArrayList();
// The generic type is not specified. The generic type defaults to Object, so you can add any instance Object to it
list.add(new A());
list.add(new B());
list.add(new C());
// The default type is Object
Object o = list.get(0);
Copy the code

This makes sense, because all classes inherit from Object, so you can add any instance Object to the list

Unbounded wildcard?

The first thing we need to understand is the concept of wildcards, right? The meaning is that it’s an unknown symbol that can represent any class.

// We find that this write fails compilation simply because the generics don't match, even though B inherits from A
List<A> listA = new ArrayList<B>(); 

// The following five lines of code are compiledList<? > list; list =new ArrayList<A>();
list = new ArrayList<B>();
list = new ArrayList<C>();
list = new ArrayList<D>();

Object o = list.get(0); // Compile passed
list.add(new A());      // Failed to compile
list.add(new B());      // Failed to compile
list.add(new C());      // Failed to compile
list.add(new D());      // Failed to compile
Copy the code

knowledge

  • Unbounded wildcard?Can withdraw or not deposit. And that makes sense, because the compiler doesn’t know?What is the specific type, so can’t save; However, any type inherits from Object, so it can fetch, but fetching defaults to Object.

Upper edge operator? extends

Let’s go ahead and do the code

List<? extends C> listC;
listC = new ArrayList<A>(); // Failed to compile
listC = new ArrayList<B>(); // Failed to compile
listC = new ArrayList<C>(); // Compile passed
listC = new ArrayList<D>(); // Compile passed

C c = listC.get(0); // Compile passed
listC.add(new C()); // Failed to compile
listC.add(new D()); // Failed to compile
Copy the code

Knowledge:

  1. Upper edge operator? extendsOnly the instance type assigned to it (in this case, the instance type assigned to listC) is limited, and the boundary includes itself.
  2. Upper edge operator? extends?It’s the same thing, the upper bound, but the compiler still doesn’t know?What type is it, so it cannot be saved; However, the upper boundary is restricted, so the type of the object that is retrieved defaults to the upper boundary

Lower boundary operator? super

List<? super B> listB;
listB = new ArrayList<A>(); // Compile passed
listB = new ArrayList<B>(); // Compile passed
listB = new ArrayList<C>(); // Failed to compile
listB = new ArrayList<D>(); // Failed to compile

Object o = listB.get(0); // Compile passed
listB.add(new A()); // Failed to compile
listB.add(new B()); // Compile passed
listB.add(new C()); // Compile passed
listB.add(new D()); // Compile passed
Copy the code

knowledge

  1. Lower boundary operator? superAs with the boundary, only the type of instance to which it is assigned, as well as the boundary itself
  2. Lower boundary operator? superYes or no, because we set the lower boundary, so we can store the types below the boundary, including the boundary itself; However, the compiler still doesn’t know when it gets it?The default type is Object.

Type erasure

The first thing to understand is that Java generics are valid at compile time and are removed at run time. Let’s look at a piece of code

// Both methods are written in the same class
public void list(List<A> listA) {}  
public void list(List<B> listB) {} 
Copy the code

Is the above code problematic? List (list ) clashed with list(list ); Both methods have the same signature after type erasure. Let’s see what happens after type erasure

public void list(List listA) {}  
public void list(List listB) {} 
Copy the code

As you can see, the two method signatures are exactly the same, so the compilation fails. Now that we understand type erasure, we need to understand one more concept

  • Generic classes do not have their own unique Class objects

For example, there is no List<A>.class or List<B>.class, but only list.class

List<A> listA = new ArrayList<A>();
List<B> listB = new ArrayList<B>();
System.out.println(listA.getClass() == listB.getClass());  / / output true
Copy the code

Generic transfer

In real world development, we often use generic passing, for example, we often need to deserialize the results of an Http request

public static <T> T fromJson(String result, Class<T> type) {
    try {
        return new Gson().fromJson(result, type);
    } catch (Exception ignore) {
        return null; }}Copy the code

So what type do we pass in, and we automatically return an object of that type

String result="xxx";
A a = fromJson(result, A.class);
B b = fromJson(result, B.class);
C c = fromJson(result, C.class);
D d = fromJson(result, D.class);
Integer integer = fromJson(result, Integer.class);
String str = fromJson(result, String.class);
Boolean boo = fromJson(result, Boolean.class);
Copy the code

What if we want to return A set, like List, which is obviously not true.

List.class = "List.class"ArrayList<A> list = fromJson(result, ArrayList<A>.class);Copy the code

So what do we do? First, let’s transform fromJson as follows:

// Type is an array type
public static <T> List<T> fromJson(String result, Class<T[]> type) {
    try {
        T[] arr = new Gson().fromJson(result, type);// Get the array first
        return Arrays.asList(arr); // Turn an array into a collection
    } catch (Exception ignore) {
        return null; }}Copy the code

That’s when we can do it

String result="xxx";
List<A> listA = fromJson(result, A[].class);
List<B> listB = fromJson(result, B[].class);
List<C> listC = fromJson(result, C[].class);
List<D> listD = fromJson(result, D[].class);
List<Integer> listInt = fromJson(result, Integer[].class);
List<String> listStr = fromJson(result, String[].class);
List<Boolean> listBoo = fromJson(result, Boolean[].class);
Copy the code

Ok, I’m going to try again. I believe most Http interfaces return data in this format:

public class Response<T> {
    private T data;
    private int code;
    private String msg;
    // Omit the get/set method
}
Copy the code

So how do we deliver that? FromJson: fromJson: fromJson: fromJson: fromJson: fromJson: fromJson: fromJson:

// Here we pass a Type directly
public static <T> T fromJson(String result, Type type) {
    try {
        return new Gson().fromJson(result, type);
    } catch (Exception ignore) {
        return null; }}Copy the code

What the hell is this Type? Click in and have a look

public interface Type {
    default String getTypeName(a) {
        returntoString(); }}Copy the code

Oh, so it’s an interface, and it only has one method, so let’s look at its implementation class

public final class Class<T> implements java.io.Serializable.GenericDeclaration.Type.AnnotatedElement {
                              // Omit the internal code
}
Copy the code

Now let’s focus on ParameterizedType, one of the implementations of the Type interface. Let’s take a look at its internal code. There are only three methods in it

public interface ParameterizedType extends Type {
    /** * For example: * List<String> List; Class} * Map<String,Long> Map; Class} * map. Entry<String,Long> Entry; {string. class, long. class} * * is returned@returnReturns all generic types */ as an array
    Type[] getActualTypeArguments();

    /** * For example: * List<String> List; Class * Map<String,Long> Map; Class * map. Entry<String,Long> Entry; Returns Entry. Class * *@returnReturns the true type */ of the generic class
    Type getRawType(a);

    /** * For example: * List<String> List; Return null * Map<String,Long> Map; Null * map. Entry<String,Long> Entry; Returns map.class * *@returnReturn the type of the generic class holder. This can be understood simply as returning the type of the outer class, or null */ if there is no outer class
    Type getOwnerType(a);
}
Copy the code

As the name implies, ParameterizedType represents a ParameterizedType.

We will define a class and implement the ParameterizedType interface as follows:

public class ParameterizedTypeImpl implements ParameterizedType {

    private Type rawType;// Real type
    private Type actualType;// Generic type

    public ParameterizedTypeImpl(Type rawType,Type actualType) {
        this.rawType = rawType;
        this.actualType = actualType;
    }

    public Type[] getActualTypeArguments() {
        return new Type[]{actualType};
    }

    public Type getRawType(a) {
        return rawType;
    }

    public Type getOwnerType(a) {
        return null; }}Copy the code

Let’s post the fromJson method again

// Here we pass a Type directly
public static <T> T fromJson(String result, Type type) {
    try {
        return new Gson().fromJson(result, type);
    } catch (Exception ignore) {
        return null; }}Copy the code

So if we want Response

, we can write it like this

Response<A> responseA = fromJson(result, new ParameterizedTypeImpl(Response.class, A.class));
Response<B> responseB = fromJson(result, new ParameterizedTypeImpl(Response.class, B.class));
Response<C> responseC = fromJson(result, new ParameterizedTypeImpl(Response.class, C.class));
Copy the code

The List

object can also be ParameterizedTypeImpl as follows:

List<A> listA = fromJson(result, new ParameterizedTypeImpl(List.class, A.class));
List<B> listB = fromJson(result, new ParameterizedTypeImpl(List.class, B.class));
List<C> listC = fromJson(result, new ParameterizedTypeImpl(List.class, C.class));
Copy the code

However, how do we get Response > if we want it? ParameterizedTypeImpl can be implemented as follows:

// First, create the Type corresponding to the List
      
        object
      
Type listAType = new ParameterizedTypeImpl(List.class, A.class);
Type listBType = new ParameterizedTypeImpl(List.class, B.class);
Type listCType = new ParameterizedTypeImpl(List.class, C.class);

// Second step, create the Type corresponding to the Response
      
       > object
      
Type responseListAType = new ParameterizedTypeImpl(Response.class, listAType);
Type responseListBType = new ParameterizedTypeImpl(Response.class, listBType);
Type responseListCType = new ParameterizedTypeImpl(Response.class, listCType);

// The third step is to obtain the corresponding Response
      
       > object from the Type object
      
Response<List<A>> responseListA = fromJson(result, responseListAType);
Response<List<B>> responseListB = fromJson(result, responseListBType);
Response<List<C>> responseListC = fromJson(result, responseListCType);

Copy the code

And then, could it be simpler? Yes, we can modify ParameterizedTypeImpl

/** * User: ljx * Date: 2018/10/23 * Time: 09:36 */
public class ParameterizedTypeImpl implements ParameterizedType {

    private final Type   rawType;
    private final Type   ownerType;
    private final Type[] actualTypeArguments;

    // Class that applies to a single generic parameter
    public ParameterizedTypeImpl(Type rawType, Type actualType) {
        this(null, rawType, actualType);
    }

    // Classes that apply to multiple generic parameters
    public ParameterizedTypeImpl(Type ownerType, Type rawType, Type... actualTypeArguments) {
        this.rawType = rawType;
        this.ownerType = ownerType;
        this.actualTypeArguments = actualTypeArguments;
    }

    List<List<String>> corresponds to get(list.class, list.class, string.class) **@paramTypes Type Array *@return ParameterizedTypeImpl
     */
    public static ParameterizedTypeImpl get(@NonNull Type rawType, @NonNull Type... types) {
        final int length = types.length;
        if (length > 1) {
            Type parameterizedType = new ParameterizedTypeImpl(types[length - 2], types[length - 1]);
            Type[] newTypes = Arrays.copyOf(types, length - 1);
            newTypes[newTypes.length - 1] = parameterizedType;
            return get(rawType, newTypes);
        }
        return new ParameterizedTypeImpl(rawType, types[0]);
    }

    // Classes that apply to multiple generic parameters
    public static ParameterizedTypeImpl getParameterized(@NonNull Type rawType, @NonNull Type... actualTypeArguments) {
        return new ParameterizedTypeImpl(null, rawType, actualTypeArguments);
    }

    public final Type[] getActualTypeArguments() {
        return actualTypeArguments;
    }

    public final Type getOwnerType(a) {
        return ownerType;
    }

    public final Type getRawType(a) {
        returnrawType; }}Copy the code

So at this point, we could write it like this

// The first step is to directly create the Type corresponding to the Response
      
       > object
      
Type responseListAType = ParameterizedTypeImpl.get(Response.class, List.class, A.class);
Type responseListBType = ParameterizedTypeImpl.get(Response.class, List.class, B.class)
Type responseListCType = ParameterizedTypeImpl.get(Response.class, List.class, C.class)

// The second step is to obtain the corresponding Response
      
       > object from the Type object
      
Response<List<A>> responseListA = fromJson(result, responseListAType);
Response<List<B>> responseListB = fromJson(result, responseListBType);
Response<List<C>> responseListC = fromJson(result, responseListCType);
Copy the code

In real world development, we may also encounter such data structures

{
    "code": 0."msg": ""."data": {
        "totalPage": 0."list": []}}Copy the code

At this point, the generic List in Response

must not be parsed properly, so we need to define another class

public class PageList<T>{
   private int totalPage;
   private List<T> list;
   // Omit the get/set method
}
Copy the code

This is how you parse the data

// The first step is to create the Type of Response
      
       >
      
Type responsePageListAType = ParameterizedTypeImpl.get(Response.class, PageList.class, A.class);
Type responsePageListBType = ParameterizedTypeImpl.get(Response.class, PageList.class, B.class)
Type responsePageListCType = ParameterizedTypeImpl.get(Response.class, PageList.class, C.class)

// The second step is to obtain the corresponding Response
      
       > object from the Type object
      
Response<PageList<A>> responsePageListA = fromJson(result, responsePageListAType);
Response<PageList<B>> responsePageListB = fromJson(result, responsePageListBType);
Response<PageList<C>> responsePageListC = fromJson(result, responsePageListCType);
Copy the code

Note: ParameterizedTypeImpl get (Type… Types) only applies to a single generic parameter, such as Map. Do not use this method for more than two generic parameters. If you need to obtain the Type of more than two generic parameters such as Map. GetParameterized (@nonnull Type rawType, @nonnull Type… ActualTypeArguments) constructor, for example:

// Obtain the Type of Map
      ,string>
Type mapType = ParameterizedTypeImpl.getParameterized(Map.class, String.classs, String.class)

// Obtain the Type of Map
      ,b>
Type mapType = ParameterizedTypeImpl.getParameterized(Map.class, A.classs, B.class)
Copy the code

To this, generics related knowledge is explained, if you have questions, please leave a message.

The ParameterizedTypeImpl class is used for generic passing in another article, RxHttp a chain to send requests.