Java generics - type erasure
1, Overview
There are many problems in the use of Java generics, such as the absence of list<string> Class, list<integer> cannot be assigned to list<number> (non covariable), strange ClassCastException, etc. The correct use of Java generics requires an in-depth understanding of some Java concepts, such as covariance, bridging methods, and the type erasure recorded in this note. Almost all Java generic processing is carried out in the compiler. The bytecode generated by the compiler does not contain generic information. The generic type information will be erased during compilation. This process is type erasure.
2, How does the compiler handle generics?
In general, a compiler handles generics in two ways:
1.Code specialization. When instantiating a generic class or generic method, a new object code (bytecode or binary code) is generated. For example, for a generic list, you may need to generate three copies of object code for string, integer, and float.
2.Code sharing. Only a unique object code is generated for each generic class; All instances of the generic class are mapped to this object code to perform type checking and type conversion when necessary.
The template in c++ is a typical Code specialization implementation. The c++ compiler generates an execution code for each generic class instance. integer list and string list are two different types in the execution code. This will cause code bloat, but experienced C + + programmers can skillfully avoid code bloat.
Another drawback of Code specialization is that it wastes space in the reference type system, because the elements in the reference type collection are essentially a pointer. It is not necessary to generate an execution code for each type. This is also the main reason why the Java compiler uses Code sharing to handle generics.
The Java compiler creates a unique bytecode representation for each generic type through Code sharing, and maps instances of the generic type to the unique bytecode representation. Mapping multiple generic class shape instances to unique bytecode representations is achieved through type erasue.
3, What is type erase?
Type erasure refers to associating generic type instances to the same bytecode through type parameter merging. The compiler only generates a bytecode for a generic type and associates its instance to that bytecode. The key to type erasure is to clear the relevant information of type parameters from generic types, and add type checking and type conversion methods when necessary.
Type erasure can be simply understood as converting generic java code to normal java code, but the compiler is more direct and converts generic java code directly to normal Java bytecode.
The main process of type erasure is as follows:
1. Replace all generic parameters with their leftmost boundary (top-level parent type) types.
2. Remove all type parameters.
Such as
copyinterface Comparable <A> { public int compareTo( A that); } final class NumericValue implements Comparable <NumericValue> { priva te byte value; public NumericValue (byte value) { this.value = value; } public byte getValue() { return value; } public int compareTo( NumericValue t hat) { return this.value - that.value; } }
-----------------
copyclass Collections { public static <A extends Comparable<A>>A max(Collection <A> xs) { Iterator <A> xi = xs.iterator(); A w = xi.next(); while (xi.hasNext()) { A x = xi.next(); if (w.compareTo(x) < 0) w = x; } return w; } } final class Test { public static void main (String[ ] args) { LinkedList <NumericValue> numberList = new LinkedList <NumericValue> (); numberList .add(new NumericValue((byte)0)); numberList .add(new NumericValue((byte)1)); NumericValue y = Collections.max( numberList ); } }
After type erasure, the type is
copyinterface Comparable { public int compareTo( Object that); } final class NumericValue implements Comparable { priva te byte value; public NumericValue (byte value) { this.value = value; } public byte getValue() { return value; } public int compareTo( NumericValue t hat) { return this.value - that.value; } public int compareTo(Object that) { return this.compareTo((NumericValue)that); } }
-------------
copyclass Collections { public static Comparable max(Collection xs) { Iterator xi = xs.iterator(); Comparable w = (Comparable) xi.next(); while (xi.hasNext()) { Comparable x = (Comparable) xi.next(); if (w.compareTo(x) < 0) w = x; } return w; } } final class Test { public static void main (String[ ] args) { LinkedList numberList = new LinkedList(); numberList .add(new NumericValue((byte)0)); , numberList .add(new NumericValue((byte)1)); NumericValue y = (NumericValue) Collections.max( numberList ); } }
After the first generic class Comparable <A> is erased, A is replaced with the leftmost boundary Object. The type parameter NumericValue of Comparable<NumericValue> is erased, but this directly leads to that NumericValue does not implement the compareTo(Object that) method of the interface Comparable, so the compiler acts as A good person and adds A bridge method.
In the second example, the boundary of the type parameter <a extends comparable<a>>a is defined. A must be a subclass of comparable<a>. According to the type erasure process, first change all the type parameters ti to the leftmost boundary comparable<a>, and then remove the parameter type A to get the final erasure result.
4, Problems caused by type erasure
It is the hidden existence of type erasure that directly leads to many generic supernatural problems.
Q1. Distinguish method signatures by instances of the same generic class—— NO!
copyimport java.util.*; public class Erasure{ public void test(List<String> ls){ System.out.println("Sting"); } public void test(List<Integer> li){ System.out.println("Integer"); } }
Compile the class,

The parameter types are obviously different. One is List<string>, and the other is List<integer>. However, secretly, after type erasure, it is all List
Q2. simultaneously catch multiple instances of the same generic exception class—— NO!
Similarly, if you define a generic class genericexception<t>, do not catch genericexception<integer> and genericexception<string>, because they are the same
Q3. are static variables of generic classes shared—— Yes!
Guess what the output of this code is?
copyimport java.util.*; public class StaticTest{ public static void main(String[] args){ GT<Integer> gti = new GT<Integer>(); gti.var=1; GT<String> gts = new GT<String>(); gts.var=2; System.out.println(gti.var); } } class GT<T>{ public static int var=0; public void nothing(T x){} }
The answer is - 2! After type erasure, all instances of generic classes are associated with the same bytecode, and all static variables of generic classes are shared.
5, Just remembe
1. there are no generic types in the virtual machine, only common classes and methods
2. the type parameters of all generic classes will be erased during compilation
3. when creating generic objects, please specify the type and let the compiler check the parameters as soon as possible (Effective Java, Article 23: please do not use primitive types in new code)
4. don't ignore the compiler warning, which means that the potential ClassCastException is waiting for you.