Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Java generics type erasure: when and what happens?

People also ask

What is type erasure and when would you use it?

Type-erasure simply means "erasing" a specific type to a more abstract type in order to do something with the abstract type (like having an array of that abstract type). And this happens in Swift all the time, pretty much whenever you see the word "Any."

What are the two 2 types of Erasure?

- Erasure is a type of alteration in document. It can be classified as chemical erasure and physical erasure. - There are many chemicals which are able to invisible the ink such as oxalic acid, sodium hydrochlorite etc.

Is Java generics compile time or runtime?

Generics are checked at compile-time for type-correctness. The generic type information is then removed in a process called type erasure. For example, List<Integer> will be converted to the non-generic type List , which ordinarily contains arbitrary objects.

Which class is used during the erasure process by the compiler?

Class Type Erasure. At the class level, the compiler discards the type parameters on the class and replaces them with its first bound, or Object if the type parameter is unbound.


Type erasure applies to the use of generics. There's definitely metadata in the class file to say whether or not a method/type is generic, and what the constraints are etc. But when generics are used, they're converted into compile-time checks and execution-time casts. So this code:

List<String> list = new ArrayList<String>();
list.add("Hi");
String x = list.get(0);

is compiled into

List list = new ArrayList();
list.add("Hi");
String x = (String) list.get(0);

At execution time there's no way of finding out that T=String for the list object - that information is gone.

... but the List<T> interface itself still advertises itself as being generic.

EDIT: Just to clarify, the compiler does retain the information about the variable being a List<String> - but you still can't find out that T=String for the list object itself.


The compiler is responsible for understanding Generics at compile time. The compiler is also responsible for throwing away this "understanding" of generic classes, in a process we call type erasure. All happens at compile time.

Note: Contrary to beliefs of majority of Java developers, it is possible to keep compile-time type information and retrieve this information at runtime, despite in a very restricted way. In other words: Java does provide reified generics in a very restricted way.

Regarding type erasure

Notice that, at compile-time, the compiler has full type information available but this information is intentionally dropped in general when the byte code is generated, in a process known as type erasure. This is done this way due to compatibility issues: The intention of language designers was providing full source code compatibility and full byte code compatibility between versions of the platform. If it were implemented differently, you would have to recompile your legacy applications when migrating to newer versions of the platform. The way it was done, all method signatures are preserved (source code compatibility) and you don't need to recompile anything (binary compatibility).

Regarding reified generics in Java

If you need to keep compile-time type information, you need to employ anonymous classes. The point is: in the very special case of anonymous classes, it is possible to retrieve full compile-time type information at runtime which, in other words means: reified generics. This means that the compiler does not throw away type information when anonymous classes are involved; this information is kept in the generated binary code and the runtime system allows you to retrieve this information.

I've written an article about this subject:

https://rgomes.info/using-typetokens-to-retrieve-generic-parameters/

A note about the technique described in the article above is that the technique is obscure for majority of developers. Despite it works and works well, most developers feel confused or uncomfortable with the technique. If you have a shared code base or plan to release your code to the public, I do not recommend the above technique. On the other hand, if you are the sole user of your code, you can take advantage of the power this technique delivers to you.

Sample code

The article above has links to sample code.


If you have a field that is a generic type, its type parameters are compiled into the class.

If you have a method that takes or returns a generic type, those type parameters are compiled into the class.

This information is what the compiler uses to tell you that you can't pass a Box<String> to the empty(Box<T extends Number>) method.

The API is complicated, but you can inspect this type information through the reflection API with methods like getGenericParameterTypes, getGenericReturnType, and, for fields, getGenericType.

If you have code that uses a generic type, the compiler inserts casts as needed (in the caller) to check types. The generic objects themselves are just the raw type; the parameterized type is "erased". So, when you create a new Box<Integer>(), there is no information about the Integer class in the Box object.

Angelika Langer's FAQ is the best reference I've seen for Java Generics.


Generics in Java Language is a really good guide on this topic.

Generics are implemented by Java compiler as a front-end conversion called erasure. You can (almost) think of it as a source-to-source translation, whereby the generic version of loophole() is converted to the non-generic version.

So, it's at compile time. The JVM will never know which ArrayList you used.

I'd also recommend Mr. Skeet's answer on What is the concept of erasure in generics in Java?