1 JAVA Type system

  • Let’s take a look at Java’s Type system (class of classes => Type). Type is a common interface for all types (prototype-class, Parameterizedtype, Array type-GenericArrayType, Type variable-typeVariable, and Primitive type-class). Class<T>, described in the previous two reflections and annotations, is an implementation of Type
  • ParameterizedType, TypeVariable, GenericArrayType, and WildcardType are subinterface classes
    • List

      is a generic type. E is a TypeVariable type and List

      is a ParameterizedType. The strings in List

      are called actual parameter types


    • To externalize a type in a generic, use? Extends or? Super for inheritance; Such asList<? extends Data>And the inside? Called the WildcardType WildcardType
    • GenericArrayType Represents an array type of an element whose type is ParameterizedType or TypeVariable, such as T[] or List

      [].
  • Annotations are new in JDK1.5, and in order to represent annotated elements, the AnnotatedElement type is added. JDK1.8 also has AnnotatedType, which associates Type with the concept of annotated elements.
  • AnnotatedType also have four sub interface, and the Type of four subinterface one-to-one correspondence, such as: ParameterizedType Type annotations are parsed into AnnotatedParameterizedType compiler:@AnTest("list")List<String>list

The concept of generics

  • Java generics is a new feature introduced in JDK1.5. Its essence is to parameterize types and solve the problem of uncertain specific object types. The data type it operates on is specified as a type parameter, which can be used in the creation of classes, interfaces, and methods called generic classes, generic interfaces, and generic methods, respectively

Generics: Postpone specifying specific types until object creation or method invocation time

Examples of generic classes and generic methods

  • Definition of generic classes
public class MainTest<T> {
    private  T param;
}
public static void main(String[] args){
        MainTest<String> data = new MainTest<String>(){};
 ParameterizedType genType1 = (ParameterizedType)data.getClass().getGenericSuperclass();  } Copy the code
  • Definition of generic methods
public class MainTest{
    public static void main(String[] args){
        printData("siting");
    }
    static  <T> T printData(T t){
 System.out.println(t);  return t;  } } Copy the code
  • Both interfaces and abstract classes can use generics

4 Type erasure

  • When creating instances of generics, the JVM erases the specific type; The bytecode generated by the compilation does not contain the type parameters of the generic type, that is, ArrayList

    and ArrayList

    are erased as ArrayLists, that is, erased as “native types”, which is a generic erase

public class MainTest {
    public static void main(String[] args){
        List<String> strArr  = new ArrayList<>();
        List<Integer> intArr  = new ArrayList<>();
        Type strClazz = strArr.getClass();
 Type intClazz = intArr.getClass();  } } Copy the code

  • To see how compiled ByteCode files are represented: idea menu -> View -> show ByteCode
public class MainTest<T> {
    T param;
    public static void main(String[] args){
        MainTest<String> test = new MainTest<>();
        test.setParam("siting");
 }  public T getParam(a) { return param; }  public void setParam(T param) { this.param = param; } } Copy the code
public class com/MainTest {
. omit  public static main([Ljava/lang/String;)V
   L0
 LINENUMBER 7 L0  NEW com/MainTest  DUP  INVOKESPECIAL com/MainTest.<init> (a)V  ASTORE 1  L1  LINENUMBER 8 L1  ALOAD 1  LDC "siting" // Call type erased setParam(Object)  INVOKEVIRTUAL com/MainTest.setParam (Ljava/lang/Object;)V  L2 . omit//getParam returns Object  public getParam(a)Ljava/lang/Object;  L0  LINENUMBER 10 L0  ALOAD 0  GETFIELD com/MainTest.param : Ljava/lang/Object;  ARETURN . omit//setParam takes Object as an input parameter  public setParam(Ljava/lang/Object;)V  L0  LINENUMBER 11 L0  ALOAD 0  ALOAD 1  PUTFIELD com/MainTest.param : Ljava/lang/Object;  RETURN .} Copy the code
  • You can see that T(String) is all converted to Object, and the initial initialized String is missing

Inheritance of generics

  • A subclass can specify generic parameters for its parent class, which can be a known class (Integer, String, and so on), or it can specify its own generic parameters
  • When generics are inherited and the parent generic parameter is specified, the additional generated ParameterizedType is used as the parent of the subclass. If no parent generic parameter is specified, the native type is inherited directly
public class MainTest<T> {
    T param;
    static public class SubTest1 extends MainTest<String>{}
    static public class SubTest2<R> extends MainTest<R>{}
    //SubTest3 inherits the primitive type
 static public class SubTest3 extends MainTest{} } Copy the code

6 Generic variable TypeVariable

  • Define a temporary name, E in Test

    as a generic parameter. Generic variables TypeVariable: Generic parameters of a generic type are TypeVariable; When a parent class specifies its own generic parameters using a child class’s generic parameters; Or when A generic attribute is defined in the generic class A

    and the generic parameter T of the generic class A

    is used, the generic parameter is defined by the compiler as the generic variable TypeVariable instead of being erased


public class MainTest<T> {
    List<T> param;
    public static void main(String[] args) throws Exception{
        Class clazz =  MainTest.class;
        TypeVariable[] typeVariable = clazz.getTypeParameters();
 / / 1  Field field = clazz.getDeclaredField("param");  ParameterizedType arrayType = (ParameterizedType)field.getGenericType();  // The generic type E of interface List
              
                is instantiated by T, so it is identified as TypeVariable
                TypeVariable variable1 = (TypeVariable)arrayType.getActualTypeArguments()[0];  / / 2  ParameterizedType type = (ParameterizedType)SubTest.class.getGenericSuperclass();  TypeVariable variable2 = (TypeVariable)type.getActualTypeArguments()[0];  }  static class SubTest<R> extends MainTest<R>{} } Copy the code

7 Parameter type ParameterizedType

public interface ParameterizedType extends Type {
    // Get the actual argument, the String in List
       
        ; If it is List
        
         , it is TypeVariable
        
       
    Type[] getActualTypeArguments(); 
    List
         
           -> List
          
         
    Type getRawType(a);  
 Type getOwnerType(a); } Copy the code
  • Note that we cannot directly obtain the type of a generic that specifies specific parameters, such asClass clazz = List<String>.classNot passed at compile time; There is also an object created directly from the generic Class new whose Class is not ParameterizedType, but the Class of the generic itself, as shown in the following example
public class MainTest<T> {
    public static void main(String[] args){
        MainTest<String> str = new MainTest<String>();
        Class variable = str.getClass();
        Type genType1 = variable.getGenericSuperclass();
 } } Copy the code

  • Only parameterized generics are recognized by the compiler as ParameterizedType. There are three ways to get ParameterizedType
// 1 when a subclass inherits a generic type, specify a specific parameter (either a known type such as String or a generic parameter of the subclass)
// 2 Gets the generic attributes defined inside the class, specifying specific generic parameters
// 3 Local code to get the ParameterizedType type from an anonymous internal subclass of the generic with specific generic parameters specified
public class MainTest<T> {
    List<T> list;
 public static void main(String[] args) throws NoSuchFieldException {  SubTest<String> str = new SubTest<>();  / / way  Class variable = str.getClass();  // The parent class is the (521)ParameterizedType type  ParameterizedType genType = (ParameterizedType)variable.getGenericSuperclass();  The native type of ParameterizedType is (479) class com.mainTest  Type clazz = genType.getRawType();  // The native type of maintest. class is (479) class com.maintest  Class rawClazz = MainTest.class;   // Generic attributes  Field field = rawClazz.getDeclaredField("list");  // ParameterizedType is of type (546).  ParameterizedType fieldType = (ParameterizedType)field.getGenericType();   3 / / way  MainTest<String> sub3 = new MainTest<String>(){};  // clazz3 is an anonymous subclass  Class clazz3 = sub3.getClass();  // The parent class is the (555)ParameterizedType type  ParameterizedType genType3 = (ParameterizedType) clazz3.getGenericSuperclass();  The native type of ParameterizedType is (479) class com.mainTest  Type type3 = genType3.getRawType();  }  public static class SubTest<R> extends MainTest<R>{}} Copy the code

8 WildcardType

Unbounded wildcards: unbounded wildcards? Can be adapted to any reference type:

  • When a generic type needs to be passed as a method parameter and its type cannot be determined. Direct use of generic variables without specific generic, easy to cause security risks; If you do type conversions in method code, you can easily get a ClassCastException error
  • Why not use Object instead of generic variables? However, there is no inheritance relationship between a generic class and a ParameterizedType. ParameterizedType Listand List

    are two different parameterizedTypes with no inheritance relationship. So you have type wildcards, right?
public static void print(List list){} 
----->>>
public static void print(List
          list){} 
Copy the code

  • Unbounded wildcards can match any type; But in use? We can’t set a value for a generic class variable because we don’t know what the type is. If you force a new value, subsequent reads are prone to ClassCastException errors. So the compiler limits the ** wildcard? ** generics can only be read, not written

The upper bound bound wildcard

  • If you want to receive a List, it can only operate on numeric elements (Float, Integer, Double, Byte, etc.). You can useList<? Subclass of extends Number >To indicate that the elements in the List are subclasses of Number
    public static void print(List<? extends Number> list) {
        Number n = new Double("1.0");
        list.add(n);
        Number tmp = list.get(0);
    }
Copy the code

  • As you can see from the picture, there is an upper bound wildcard, because the specific type is uncertain, and it can only be read but not written

The lower bound qualifies the wildcard

class Parent{ }
class Child extends Parent{ }
public class MainTest<T> {
    T param;
    public static void main(String[] args){
 MainTest<? super Child> parent_m = new MainTest<>();  parent_m.setParam(new Child());  Object parent = parent_m.getParam();  }  public T getParam() { return param; }  public void setParam(T param) { this.param = param; } } Copy the code

  • If we define whose parent the wildcard is, then the lower bound defines the wildcard; Such wildcards are readable and writable, and can be converted to any parent class without a ClassCastException.
  • Personal guess: Is it because generic downward conversions of wildcards and upper-bound wildcards are prone to ClassCastException errors and up-conversions of lower-bound wildcards are non-classcastException errors, so the Java specification prevents the former from compiling and the latter from compiling?

9 GenericArrayType

public interface GenericArrayType extends Type {
// Get the array element type: A<T> (A<T>[]) or T (T[])    Type getGenericComponentType();
}
Copy the code
  • GenericArrayType is a subinterface to ParameterizedType and TypeVariable arrays, such as Test

    [][], T[], and GenericArrayType
public class MainTest<T> {
    T[] param;
    public static void main(String[] args) throws Exception{
        Class clazz =  MainTest.class;
        Field field = clazz.getDeclaredField("param");
 GenericArrayType arrayType = (GenericArrayType)field.getGenericType();  TypeVariable variable = (TypeVariable) arrayType.getGenericComponentType();  } } Copy the code


Corrections are welcome

Pay attention to the public account, communicate together, wechat search: sneak forward

  • Generics are as simple as that
  • Essential foundation for Java programmers: Generic parsing
  • Java gets a detailed description of type instances of generics