Rectangle 27 0

Java generics type erasure when and what happens?


@Richard Indeed, excellent article! You could add that local classes work too and that, in both cases (anonymous and local classes), information about the desired type argument is kept only in case of direct access new Box<String>() {}; not in case of indirect access void foo(T) {...new Box<T>() {};...} because the compiler does not keep type information for the enclosing method declaration.

@RichardGomes PLEASE update the link

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.

Actually, they didn't maintain both binary and source compatibility: oracle.com/technetwork/java/javase/compatibility-137462.html Where can I read more about their intention? Docs say that it uses type erasure, but doesn't say why.

I've written an article about this subject:

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.

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.

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).

The article above has links to sample code.

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
Rectangle 27 0

Java generics type erasure when and what happens?


Actually, it is the formal generic type of the fields and methods that are compiled into the class, i.e., typicall "T". To obtain the real type of the generic type, you must use the "anonymous-class trick".

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

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.

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.

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

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.

Note
Rectangle 27 0

Java generics type erasure when and what happens?


@Richard Indeed, excellent article! You could add that local classes work too and that, in both cases (anonymous and local classes), information about the desired type argument is kept only in case of direct access new Box<String>() {}; not in case of indirect access void foo(T) {...new Box<T>() {};...} because the compiler does not keep type information for the enclosing method declaration.

@RichardGomes PLEASE update the link

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.

Actually, they didn't maintain both binary and source compatibility: oracle.com/technetwork/java/javase/compatibility-137462.html Where can I read more about their intention? Docs say that it uses type erasure, but doesn't say why.

I've written an article about this subject:

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.

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.

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).

The article above has links to sample code.

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
Rectangle 27 0

Java generics type erasure when and what happens?


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

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

@Rogerio: As I replied to your comment, I believe you're getting confused between being able to get the type of a variable and being able to get the type of an object. The object itself doesn't know its type argument, even though the field does.

@Rogerio: How do you know where the object has come from though? If you have a parameter of type List<? extends InputStream> how can you know what type it was when it was created? Even if you can find out the type of the field the reference has been stored in, why should you have to? Why should you be able to get all the rest of the information about the object at execution time, but not its generic type arguments? You seem to be trying to make type erasure out to be this tiny thing which doesn't affect developers really - whereas I find it to be a very significant problem in some cases.

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

But type erasure IS a tiny thing which doesn't really affect developers! Of course, I can't speak for others, but in MY experience it never was a big deal. I actually take advantage of runtime type information in the design of my Java mocking API (JMockit); ironically, .NET mocking APIs seem to take less advantage of the generic type system available in C#.

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.

No, even in the use of a generic type there may be metadata available at runtime. A local variable is not accessible through Reflection, but for a method parameter declared as "List<String> l", there will be type metadata at runtime, available through the Reflection API. Yep, "type erasure" is not as simple as many people think...

Of course, just looking at the object itself you can't know that it's a List<String>. But objects don't just appear from nowhere. They are created locally, passed in as a method invocation argument, returned as the return value from a method call, or read from a field of some object... In all these cases you CAN know at runtime what the generic type is, either implicitly or by using the Java Reflection API.

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:

Note
Rectangle 27 0

Java generics type erasure when and what happens?


ArrayList

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.

Note
Rectangle 27 0

Java generics type erasure when and what happens?


Actually, it is the formal generic type of the fields and methods that are compiled into the class, i.e., typicall "T". To obtain the real type of the generic type, you must use the "anonymous-class trick".

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

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.

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.

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

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.

Note
Rectangle 27 0

Java generics type erasure when and what happens?


ArrayList

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.

Note
Rectangle 27 0

Java generics type erasure when and what happens?


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

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

@Rogerio: As I replied to your comment, I believe you're getting confused between being able to get the type of a variable and being able to get the type of an object. The object itself doesn't know its type argument, even though the field does.

@Rogerio: How do you know where the object has come from though? If you have a parameter of type List<? extends InputStream> how can you know what type it was when it was created? Even if you can find out the type of the field the reference has been stored in, why should you have to? Why should you be able to get all the rest of the information about the object at execution time, but not its generic type arguments? You seem to be trying to make type erasure out to be this tiny thing which doesn't affect developers really - whereas I find it to be a very significant problem in some cases.

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

But type erasure IS a tiny thing which doesn't really affect developers! Of course, I can't speak for others, but in MY experience it never was a big deal. I actually take advantage of runtime type information in the design of my Java mocking API (JMockit); ironically, .NET mocking APIs seem to take less advantage of the generic type system available in C#.

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.

No, even in the use of a generic type there may be metadata available at runtime. A local variable is not accessible through Reflection, but for a method parameter declared as "List<String> l", there will be type metadata at runtime, available through the Reflection API. Yep, "type erasure" is not as simple as many people think...

Of course, just looking at the object itself you can't know that it's a List<String>. But objects don't just appear from nowhere. They are created locally, passed in as a method invocation argument, returned as the return value from a method call, or read from a field of some object... In all these cases you CAN know at runtime what the generic type is, either implicitly or by using the Java Reflection API.

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:

Note