Type Erasure – Generics

11.11 Type Erasure

Understanding translation by type erasure aids in understanding the restrictions and limitations that arise when using generics in Java. Although the compiler generates generic-free bytecode, we can view the process as a source-to-source translation that generates non-generic code from generic code.

The translated code has no information about type parameter. That is, the type parameters have been erased—hence the term type erasure. This involves replacing the usage of the type parameters with concrete types, and inserting suitable type conversions to ensure type correctness. In certain situations, bridge methods are also inserted for backward compatibility.

Translation by Type Erasure

The process of determining the erasure of a type—that is, what a type in the source code should be replaced with—uses the following rules:

Drop all type parameter specifications from parameterized types.

Replace any type parameter as follows:

  1. Replace it with the erasure of its bound, if it has one.
  2. Replace it with Object, if it has none.
  3. Replace it with the erasure of the first bound, if it has multiple bounds.

Table 11.4 shows examples of translation by erasure for some representative types, and the rules that are applied.

Table 11.4 Examples of Type Erasure

TypeErasureRule no.
List<E>
List<Integer>
List<String>
List<List<String>>
List<? super Integer>
List<? extends Number>
List1
List<Integer>[]List[]1
ListList1
intintFor any primitive type
IntegerIntegerFor any non-generic type
class Subclass
  extends Superclass
  implements
  Comparable<Subclass> {…}

class Subclass
  extends Superclass
  implements
  Comparable {…}

1
public static
  <T extends Comparable<? super T>>
  T
  max(T obj1,
     T obj2)
  { … }

public static

  Comparable
  max(Comparable obj1,
      Comparable obj2)
  { … }

2a. The first bound is Comparable.
public static <T> T
  doIt(T t)
  { T lv = t; }

public static Object
  doIt(Object t)
  { Object lv = t; }

2b
T extends MyClass &
  Comparable<T> &
  Serializable

MyClass

2c. The first bound is MyClass.

The following code mixes legacy and generic code. Note that a ClassCastException is expected at (5) because the type-safety of the stack of String has been compromised.

Click here to view code image

// Pre-erasure code
List<String> strList = new ArrayList<>(); // (0)
List list = strList;       // (1) Assignment to non-generic reference is ok.
strList = list;            // (2) warning: unchecked conversion
strList.add(“aha”);        // (3) Method call type-safe.
list.add(23);              // (4) warning: [unchecked] unchecked call to add(E)
                           //     as a member of the raw type java.util.List
System.out.println(strList.get(1).length());  // (5) ClassCastException

It is instructive to compare the corresponding lines of code in the pre-erasure code above and the post-erasure results shown below. A cast is inserted to convert from Object type to String type at (5′). This is necessary because post-erasure code can only get an Object from the list, and in order to call the length() method, the reference value of this object must be converted to String. It is this cast that is the cause of the exception at runtime.

Click here to view code image

// Post-erasure code
List strList = new ArrayList();                        // (0′)
List list = strList;                                   // (1′)
strList = list;                                        // (2′)
strList.add(“aha”);                                    // (3′)
list.add(Integer.valueOf(23));                         // (4′)
System.out.println(((String)strList.get(1)).length()); // (5′) Cast inserted.

Comments

Leave a Reply

Your email address will not be published. Required fields are marked *