Over a million developers have joined DZone.
{{announcement.body}}
{{announcement.title}}

On Java Generics and Erasure

DZone's Guide to

On Java Generics and Erasure

· Java Zone ·
Free Resource

Verify, standardize, and correct the Big 4 + more– name, email, phone and global addresses – try our Data Quality APIs now at Melissa Developer Portal!

 “Generics are erased during compilation” is common knowledge (well, type parameters and arguments are actually the ones erased). That happens due to “type erasure”. But it’s wrong that everything specified inside the <..> symbols is erased, as many developers are assuming. See the code below:

public class ClassTest {
  public static void main(String[] args) throws Exception {
    ParameterizedType type = (ParameterizedType)
       Bar.class.getGenericSuperclass();
    System.out.println(type.getActualTypeArguments()[0]);
     
    ParameterizedType fieldType = (ParameterizedType)
        Foo.class.getField("children").getGenericType();
    System.out.println(fieldType.getActualTypeArguments()[0]);
     
    ParameterizedType paramType = (ParameterizedType)
        Foo.class.getMethod("foo", List.class)
        .getGenericParameterTypes()[0];
    System.out.println(paramType.getActualTypeArguments()[0]);
     
    System.out.println(Foo.class.getTypeParameters()[0]
        .getBounds()[0]);
  }
   
  class Foo<E extends CharSequence> {
    public List<Bar> children = new ArrayList<Bar>();
    public List<StringBuilder> foo(List<String> foo) {return null; }
    public void bar(List<? extends String> param) {}
  }
    
  class Bar extends Foo<String> {}
}

Do you know what that prints?

class java.lang.String
class ClassTest$Bar
class java.lang.String
class java.lang.StringBuilder
interface java.lang.CharSequence

You see that every single type argument is preserved and is accessible via reflection at runtime. But then what is “type erasure”? Something must be erased? Yes. In fact, all of them are, except the structural ones – everything above is related to the structure of the classes, rather than the program flow. In other words, the metadata about the type arguments of a class and its field and methods is preserved to be accessed via reflection.

The rest, however, is erased. For example, the following code:

List<String> list = new ArrayList<>();
Iterator<String> it = list.iterator();
while (it.hasNext()) {
   String s = it.next();
}

will actually be transformed to this (the bytecode of the two snippets is identical):

List list = new ArrayList();
Iterator it = list.iterator();
while (it.hasNext()) {
   String s = (String) it.next();
}

So, all type arguments you have defined in the bodies of your methods will be removed and casts will be added where needed. Also, if a method is defined to accept List<T>, this T will be transformed to Object (or to its bound, if such is declared. And that’s why you can’t do new T(). (by the way, one open question about this erasure)

So far we covered the first two points of the type erasure definition. The third one is about bridge methods. And I’ve illustrated it with this stackoverflow question (and answer).

Two “morals” of all this. First, java generics are complicated. But you can use them without understanding all the complications.

Second, do not assume that all type information is erased – the structural type arguments are there, so make use of them, if needed (but don’t be over-reliant on reflection)

Developers! Quickly and easily gain access to the tools and information you need! Explore, test and combine our data quality APIs at Melissa Developer Portal – home to tools that save time and boost revenue. Our APIs verify, standardize, and correct the Big 4 + more – name, email, phone and global addresses – to ensure accurate delivery, prevent blacklisting and identify risks in real-time.

Topics:

Published at DZone with permission of

Opinions expressed by DZone contributors are their own.

{{ parent.title || parent.header.title}}

{{ parent.tldr }}

{{ parent.urlSource.name }}