Before introducing type erasure, take a look at the following code:

public static void genericRemove(a) throws InvocationTargetException, IllegalAccessException, NoSuchMethodException {
        List<Integer> list = new ArrayList<>();
        list.add(1);

        Method addMethod = list.getClass().getMethod("add", Object.class);
        addMethod.invoke(list , "ff");
    }
Copy the code

Do you think it will work? The answer is yes. You may be wondering if the list is restricted to an Integer type by generics and how it can be executed successfully. Generics are checked for type safety at compile time by reflection, which takes effect at run time, leading to the concept of type erasure.

What type erasure is

Many articles talk about Type Erasure in terms of generic Erasure. The official website uses Type Erasure as a literal translation.

In programming languages, type erasure is the load-time process by which explicit type comments are removed from a program before it is executed at runtime. Operational semantics that do not require program concomitant types are called type erasing semantics, in contrast to type passing semantics. The possibility of erasing semantics for types is an abstraction principle that ensures that the runtime execution of a program is independent of type information. —- Wikipedia

Generics were introduced into the Java language to provide stricter type checking at compile time and support generic programming. To implement generics, the Java compiler applies type erasure to:

  • If type parameters are unbounded, replace all type parameters in a generic type with their boundary or Object. Therefore, the generated bytecode contains only ordinary classes, interfaces, and methods.
  • Type conversions are inserted when necessary to preserve type safety.
  • Bridge methods are generated to preserve polymorphism in extended generic types.

Type erasure ensures that no new classes are created for parameterized types; Therefore, generics incur no runtime overhead.

In layman’s terms, the JVM is unaware of the existence of generics and, after being translated into a. Class file, will replace qualified generics with object classes or boundary classes (such as a parent, or a parent shared by several generics). Generics were introduced in JDK 1.5, with a type erasure to make them compatible with previous versions of JDK 1.5.

To find out

Let’s look at type erasure in bytecode form. GenericClass genericClass.java

public class GenericClass<T> {

    private T data;

    public T getData(a) {
        return data;
    }

    public void setData(T data) {
        this.data = data; }}Copy the code

Create another test class:

public class Test {
    public static void main(String[] args) throws InvocationTargetException, IllegalAccessException, NoSuchMethodException {
        GenericClass<String> stringGenericClass = new GenericClass<>();
        stringGenericClass.setData("Generic class data"); System.out.println(stringGenericClass.getData()); }}Copy the code

View bytecode:

Compiled from "Test.java"
public class generic.Test {
  public generic.Test();
    Code:
       0: aload_0
       1: invokespecial #1                  // Method java/lang/Object."<init>":()V
       4: return

  public static void main(java.lang.String[]) throws java.lang.reflect.InvocationTargetException, java.lang.IllegalAccessException, java.lang.NoSuchMethodException;
    Code:
       0: new           #2                  // class generic/GenericClass
       3: dup
       4: invokespecial #3                  // Method generic/GenericClass."<init>":()V
       7: astore_1
       8: aload_1
       9: ldc           #4                  // String the generic class data
      11: invokevirtual #5                  // Method generic/GenericClass.setData:(Ljava/lang/Object;) V
      14: getstatic     #6                  // Field java/lang/System.out:Ljava/io/PrintStream;
      17: aload_1
      18: invokevirtual #7                  // Method generic/GenericClass.getData:()Ljava/lang/Object;
      21: checkcast     #8                  // class java/lang/String
      24: invokevirtual #9                  // Method java/io/PrintStream.println:(Ljava/lang/String;) V
      27: return
}

Copy the code

GenericClass uses generics to qualify its type to String, and its getter/setter methods should look something like this

public String getData(a) {
        return data;
}

public void setData(String data) {
        this.data = data;
}
Copy the code

But through the bytecode Line 11 Method generic/GenericClass setData (Ljava/lang/Object) V line 18 Method generic/GenericClass getData () Ljava/lang/Object; You can see that the getter/setter method of GenericClass becomes something like this, and the JVM type erasing the generics, replacing String with Object.

public Object getData(a) {
        return data;
}

public void setData(Object data) {
        this.data = data;
}
Copy the code

Type erasure rules

During type erasing, the Java compiler erases all type parameters, replacing each type parameter with its first boundary if it is bounded, and Object if it is unbounded. Here are three chestnuts to explain this sentence.

  1. Type parameters are unbounded

In the case of GenericClass< T>, type T has no apparent inheritance or implementation of any interface or class, so type erasers replace T with Object.

  1. Type parameters are bounded

Change GenericClass to the following form:

public class GenericClass<T extends Number> {

    private T data;
    private GenericClass<T> next;
    // omit the getter method
    public void setNext(GenericClass<T> next) {
        this.next = next;
    }
    public void setData(T data) {
        this.data = data; }}Copy the code

The test class:

public class Test {

    public static void main(String[] args)  {
        GenericClass<Integer> stringGenericClass = new GenericClass<>();
        stringGenericClass.setData(1);
        stringGenericClass.setNext(newGenericClass<>()); System.out.println(stringGenericClass.getData()); }}Copy the code

Look at the bytecode again:

Compiled from "Test.java"
public class generic.Test {
  public generic.Test();
    Code:
       0: aload_0
       1: invokespecial #1                  // Method java/lang/Object."<init>":()V
       4: return

  public static void main(java.lang.String[]);
    Code:
       0: new           #2                  // class generic/GenericClass
       3: dup
       4: invokespecial #3                  // Method generic/GenericClass."<init>":()V
       7: astore_1
       8: aload_1
       9: iconst_1
      10: invokestatic  #4                  // Method java/lang/Integer.valueOf:(I)Ljava/lang/Integer;
      13: invokevirtual #5                  // Method generic/GenericClass.setData:(Ljava/lang/Number;) V
      16: aload_1
      17: new           #2                  // class generic/GenericClass
      20: dup
      21: invokespecial #3                  // Method generic/GenericClass."<init>":()V
      24: invokevirtual #6                  // Method generic/GenericClass.setNext:(Lgeneric/GenericClass;) V
      27: getstatic     #7                  // Field java/lang/System.out:Ljava/io/PrintStream;
      30: aload_1
      31: invokevirtual #8                  // Method generic/GenericClass.getData:()Ljava/lang/Number;
      34: invokevirtual #9                  // Method java/io/PrintStream.println:(Ljava/lang/Object;) V
      37: return
}
Copy the code

From the bytecode can see Method generic/GenericClass setNext: (Lgeneric/GenericClass;) V Method generic/GenericClass.setData:(Ljava/lang/Number;) V/T is restricted to being a subclass of Number, so the type of the data property is replaced by Number during type erasure.

General ability, limited level, if any mistakes, please point out. Give it a thumbs up if it helps you