preface

This article is about deep learning generics from the bytecode level, which is also a common interview in large factories

We define the following class as shown in the diagram:



Covariant and contravariant generics

define

The role of the generic extend

Please browse the following code first

 	        List<? extends Female> extenfemalesList1 = new ArrayList<Female>();
// List
       extenfemalesList2 = new ArrayList
      
       (); //error
      
// List
       extenfemalesList3 = new ArrayList(); //error
        List<? extends Female> extenfemalesList4 = new ArrayList<FemaleTeacher>();
        Female female = extenfemalesList1.get(0);
// extenfemalesList1.add(new Female()); //erro
Copy the code

List<? extends Female>Represents that the collection is loadedfemaleThe collection of objects below. So this set could be onenew ArrayList<Female>().new ArrayList<FemaleTeacher>()

For this reason, the data we extract must be a Female object or its own class. So the following code compiles successfully:

   Female female = extenfemalesList1.get(0);
Copy the code

The following code must fail because List
Does not determine whether your collection is new ArrayList

() or new ArrayList

(), If you are new ArrayList

() then the insertion must cause an error, so here the compiler cannot tell and therefore throws an error.


 extenfemalesList1.add(new Female());
Copy the code

Summary: The generic extend function is the concept of covariant, which is used to indicate that its collection may be a subset of inheriting generics.

The role of the generic super

    List<? super Female> extenfemalesList1 = new ArrayList<Female>();
        List<? super Female> extenfemalesList2 = new ArrayList<Human>();
        List<? super Female> extenfemalesList3 = new ArrayList<Object>();
// List
       extenfemalesList4 = new ArrayList
      
       (); //erro
      
// Female female = extenfemalesList1.get(0); //erro
        extenfemalesList1.add(new Female()); 
        Object female = extenfemalesList1.get(0);
Copy the code

The concept diagram is as follows:



List<? super Female>A collection type that indicates that the collection may be the parent of the generic.

So the following code compiles smoothly

extenfemalesList1.add(new Female());// No parent collection type violates the semantics
Object female = extenfemalesList1.get(0);// The parent collection type must be an Object
Copy the code

Violation of semantic error code

	// The Female type cannot be determined
    Female female = extenfemalesList1.get(0);//erro
Copy the code

Summary: The generic super is the contravariant concept

Generic erasure

What is generic erasure? The JVM runtime treats generics as objects, and note that this is for the JVM runtime. But the generic information remains at the bytecode level, but we store it in different places depending on the syntax.

Generic function

 static  void test(List<FemaleTeacher> listParameter) {
        FemaleTeacher femaleTeacher = listParameter.get(0);
        System.out.println(femaleTeacher);
    }
Copy the code

Can the above code get the specific type of the generic at run time?

Where is generic erasure represented?

JVM call instructions:



We can see the final implementation pseudocode for the JVM as follows:

   static  void test(List<Object> listParameter) {
        Object femaleTeacher = listParameter.get(0);
        FemaleTeacher femaleTeacher1 = (FemaleTeacher) femaleTeacher;
        System.out.println(femaleTeacher1);

    }
Copy the code

This is generic erasure. But if you look closely, there is oneLocalVariableTypeTableThe inside shows generics

LocalVariableTypeTableStored in the bytecode method areacodetypeattribute_infotheattributeIn the.

So we can get to by reflection Specific can see other articles on the Internet at https://www.cnblogs.com/wwjj4811/p/12592443.html

Generic functions have one more function than other functions that do not contain genericssignatureThe type ofattribute_infoAttribute existsmedhod_infoIn the





Here’s the explanationdescriptorandsignatureThe difference between:

If you encounter these two parameters when writing the ASM framework, if your bytecode methods don’t contain genericssignatureJust write an empty string.

A generic class

public class MyClassA<T> {


    public void say(T t) { System.out.println(t); }}public class MyClassB extends MyClassA<Human> {}Copy the code

Let’s just look at itMyClassBInformation after compilation:

Generic classes will be in bytecodeattribute_infoEnter one more tableattriute_info



So you can get the generics of the class by following the link below

https://www.cnblogs.com/one777/p/7833789.html
https://blog.csdn.net/u011082160/article/details/101025884
Copy the code

type

Preliminary knowledge

As we know from the previous analysis, the JVM only implements generic erasure at runtime, and generic information is stored in the class. To retrieve this information,java1.5launchedTypeTypes let us fetch by reflection.

A little bit of a conclusion or a little bit of knowledge here, if you get type class by reflection, then there is no generic information.

I’ll give you a little Demo that I hope will help you understand


interface GeneircInteface {}
class MyTestClass implements GeneircInteface {}

public class Java {
    public static void main(String[] args) {
    	//getGenericInterfaces get the generic information on the interface, and return an array of 0 if the interface is not implemented
    	// You can use getGenericSuperclass if you want to inherit the generic information of the class
        Type[] genericInterfaces = MyTestClass.class.getGenericInterfaces();
        boolean isClassType = genericInterfaces[0] instanceof Class;
        // The generic information represented by ParameterizedType is described later. At this stage, we only need to know that it stores generic information
        boolean isGenericType = genericInterfaces[0] instanceof ParameterizedType;
        //true
        System.out.println("isClass:" + isClassType);
        //false
        System.out.println("isGenericType:"+ isGenericType); }}Copy the code

The case interface above has no generic information, let’s add a small generic to see


interface GeneircInteface<T> {}
class MyTestClass implements GeneircInteface<String> {}

public class Java {
    public static void main(String[] args) {
        Type[] genericInterfaces = MyTestClass.class.getGenericInterfaces();
        boolean isClassType = genericInterfaces[0] instanceof Class;
        boolean isGenericType = genericInterfaces[0] instanceof ParameterizedType;
        //false
        System.out.println("isClass:" + isClassType);
        //true
        System.out.println("isGenericType:"+ isGenericType); }}Copy the code

ParameterizedType

//ParameterizedType.java
/** * indicates a generic declaration type: Class MyTestClass {* class GeneircInteface
      
        {* *} *} * * * We get the class class GeneircInteface
       
         ParameterizedType * ParameterizedType specifies the class GeneircInteface, which corresponds to getRawType * ParameterizedType getActualTypeArguments * ParameterizedType getActualTypeArguments * ParameterizedType getActualTypeArguments * ParameterizedType returns MyTestClass Corresponds to the getOwnerType function **/
       ,b>
      ,b>
public interface ParameterizedType extends Type {
    ** interface GeneircInteface
      
        {} * Returns the array of types corresponding to A and B */
      ,b>
    Type[] getActualTypeArguments();

    /** * The generic class * such as interface GeneircInteface
      
        {} * Returns GeneircInteface. Only type */ of type class can be returned
      ,b>
    Type getRawType(a);

   	Class MyTestClass {* class GeneircInteface
      
        {**} ** We get the class class ParameterizedType * getOwnerType of GeneircInteface
       
         returns MyTestClass. Only type **/ of class type can be returned
       ,b>
      ,b>
    Type getOwnerType(a);
}
Copy the code

Case study:

package main;
class MyTestClass {
   static class GeneircInteface<A.B> {}}Copy the code
public class Java {

    public static void main(String[] args) throws NoSuchMethodException {
        Method method = new Java().getClass().getMethod("applyMethod", MyTestClass.GeneircInteface.class);
        Type[] types = method.getGenericParameterTypes();
        ParameterizedType pType = (ParameterizedType)types[0];
        // Returns the owner type, printed as
        // Output class main.mytestClass
        System.out.println(pType.getOwnerType());
        // This can only be Class Type.
        System.out.println(pType.getOwnerType() instanceof  Class);


        // Since there are two generics, the array size should be 2
        // What Type is returned
        Type[] actualTypeArguments = pType.getActualTypeArguments();

        2 / / output
        System.out.println(actualTypeArguments.length);
        
        // Return the class of GeneircInteface, where ParameterizedType is no longer available
        //Class is a subtype of Type
        / / output true
        System.out.println(pType.getRawType() instanceof  Class);
    }
    public static <T,C> void applyMethod(MyTestClass.GeneircInteface<T,C> list){}}Copy the code

This case reference ParameterizedType. GetOwnerType () function is how to do?

GenericArrayType

The generic Type of the array Type.

//GenericArrayType.java
* class GeneircInteface
      
        {} * An array: * GeneircInteface
       
         [] arr; * GenericArrayType represents the generic information on this array **/
       ,string>
      ,b>
public interface GenericArrayType extends Type {
 	// The Type of the generic class stored in the array
 	GeneircInteface
      
        [] arr; Return the type of GeneircInteface
      ,string>
    Type getGenericComponentType(a);
}
Copy the code
package main;
class MyTestClass {
   static class GeneircInteface<A.B> {}}Copy the code

public class Java {

    // Declare the generic concrete type
    MyTestClass.GeneircInteface<Object, String>[] arr;
    // No generic concrete type is declared
    MyTestClass.GeneircInteface[] arr2;

    List<Integer>[] arr3;


    public static void main(String[] args) throws NoSuchMethodException, NoSuchFieldException {
        Field arr = Java.class.getDeclaredField("arr");
        Field arr2 = Java.class.getDeclaredField("arr2");
        Field arr3 = Java.class.getDeclaredField("arr3");

        Type genericType = arr.getGenericType();
        Type genericType2 = arr2.getGenericType();
        Type genericType3 = arr3.getGenericType();
        / / output true
        System.out.println(genericType instanceof GenericArrayType);
        If no generic type is declared, the type returned is class
        System.out.println(genericType2 instanceof GenericArrayType);
        / / output true
        System.out.println(genericType2 instanceof Class);
        / / output true
        System.out.println(genericType3 instanceof GenericArrayType);

        GenericArrayType genericTypeObj = (GenericArrayType) genericType;
        // return here
        Type genericComponentType = genericTypeObj.getGenericComponentType();

        // ParameterizedType: true
        // ParameterizedType of GeneircInteface is returned
        System.out.println(genericComponentType instanceof ParameterizedType);


        GenericArrayType genericTypeObj3 = (GenericArrayType) genericType;
        // return true, as above
        System.out.println(genericTypeObj3 instanceofParameterizedType); }}Copy the code

TypeVariable

// Represents the original generic declaration of a class. Here ParameterizedType must be distinguished
//ParameterizedType is used to get classes that are explicitly generic and cannot be gotten otherwise. For example, interface MyInt< k extends String> cannot be gotten
// TypeVariable gets interface MyInt< k extends String>, including the name k and the upper bound String
public interface TypeVariable<D extends GenericDeclaration> extends Type.AnnotatedElement {
  
    // Return the upper bound defined by the generics
  	// interface MyInt< k extends String>
  	// Returns a String.
  	// Why array? Maybe forward compatibility??
    Type[] getBounds();

    // Define the class in which the generic type resides
    //interface MyInt< k extends String>
    // return the class of MyInt
    D getGenericDeclaration(a);
	// Return the name of the original definition of the generic
	//
	// interface MyInt< k extends String>
	// 这里就是返回 K
	// 
    String getName(a);

  	// There is an extra API in 1.8, but I don't understand it so I won't explain it
    AnnotatedType[] getAnnotatedBounds();
}
Copy the code
 public static void main(String[] args) throws NoSuchMethodException, NoSuchFieldException {

        TypeVariable<Class<MyTestClass.GeneircInteface>>[] typeParameters = MyTestClass.GeneircInteface.class.getTypeParameters();
        // Since there are two generics, this returns 2
        System.out.println(typeParameters.length);

        TypeVariable<Class<MyTestClass.GeneircInteface>> typeParameter_0 = typeParameters[0];

        TypeVariable<Class<MyTestClass.GeneircInteface>> typeParameter_1 = typeParameters[1];

        // Get the letter to declare generics
        / / o k
        System.out.println(typeParameter_0.getName());
        / / output B
        System.out.println(typeParameter_1.getName());

        Type[] bounds = typeParameter_0.getBounds();
        // The length of the array is 1
        System.out.println(bounds.length);

        Type bound = bounds[0];
        // Output last class java.lang.string
        System.out.println(bound);
        //true
        System.out.println(bound instanceof  Class);

        // Output class java.lang.class
        System.out.println(typeParameter_0.getGenericDeclaration());

    }
Copy the code

WildcardType

//WildcardType.java
// The types described above provide sufficient generics information in most cases, but the one missing is the upper and lower bounds of generics
// Define the upper bound :List
      
// Define the lower bound :List
       listOne
public interface WildcardType extends Type {
    // Get the upper bound of the generic type. If it is not set then it is Object, which means the array must be 1 in length
    // Why array type, perhaps for forward compatibility
    Type[] getUpperBounds();
    // Get the lower bound of the generic type. If not set it is an array of length 0
    // Why array type, perhaps for forward compatibility
    Type[] getLowerBounds();
  
}
Copy the code

public class Java {


    public static void main(String[] args) throws NoSuchMethodException, NoSuchFieldException {

        Method testFunction = Java.class.getDeclaredMethod("testFunction", List.class, List.class);

        // The array length is 2 because there are two arguments
        Type[] genericParameterTypes = testFunction.getGenericParameterTypes();
        2 / / output
        System.out.println(genericParameterTypes.length);

        // It must be a ParameterizedType because the parameter contains it
        ParameterizedType genericParameterType = (ParameterizedType) genericParameterTypes[0];
        Returns specific generic information via getActualTypeArguments of ParameterizedType
        Type[] actualTypeArguments = genericParameterType.getActualTypeArguments();
        // Because there is only one generic 
       so only array length 1
        / / output 1
        System.out.println(actualTypeArguments.length);

        / / 
       contains wildcards so type must be WildcardType
        WildcardType actualTypeArgument = (WildcardType) actualTypeArguments[0];
        / / output? super java.lang.String
        System.out.println(actualTypeArgument);
        / / 
       declares a lower bound, so lowerBounds is not null and has length 1
        // Why array instead of a Type I'm not sure, maybe forward compatibility
        Type[] lowerBounds = actualTypeArgument.getLowerBounds();
        / / output 1
        System.out.println(lowerBounds.length);

        // The lower bound is String and does not contain generic information. Type can be class
        Type lowerBound = lowerBounds[0];
        / / output
        System.out.println(lowerBound instanceof Class);

        // Get previous information without declaring that the default is Object
        Type[] upperBounds = actualTypeArgument.getUpperBounds();
        / / output 1
        System.out.println(upperBounds.length);
        // Output class java.lang.object
        System.out.println(upperBounds[0]);
        / / output true
        System.out.println(upperBounds[0] instanceof Class);

        List
       listTwo
        ParameterizedType genericParameterTypeTwo = (ParameterizedType) genericParameterTypes[1];
        Type[] actualTypeArgumentsTwo = genericParameterTypeTwo.getActualTypeArguments();

        WildcardType wildcardTypeTwo = (WildcardType) actualTypeArgumentsTwo[0];

        / / note that the List 
       defines last, but the lower bound is not defined so it prints 0
        / / 0
        System.out.println(wildcardTypeTwo.getLowerBounds().length);
        // The upper bound is defined so the length here is 1
        / / output 1
        System.out.println(wildcardTypeTwo.getUpperBounds().length);
    }


    void testFunction(List<? super String> listOne, List<? extends String> listTwo) {}}Copy the code

reference

In Java Type in Java Type, Type, rounding ParameterizedType. GetOwnerType () function is how to do?