views:

2114

answers:

5

I read about Java's type erasure on Sun's website (http://java.sun.com/docs/books/tutorial/java/generics/erasure.html).

I have the following question :

When does type erasure occur - at compile time / runtime:when the class is loaded / runtime:when the class is instantiated.

A lot of sites (including the Sun tutorial mentioned above) say type erasure occurs at compile time. If the type information is completely removed at compile time, how does the JDK check type compatibility when a method using generics is invoked with no type information or wrong type information.

Consider the following example : say class A has a method empty(Box<T extends Number> b). We compile A.java and get the class file A.class. Now we create another class B which invokes the method empty with a non-parameterized argument - empty(new Box()). If we compile B.java with A.class in the classpath, javac is smart enough to raise a warning. So A.class has some type information stored in it.

My guess would be that type erasure occurs when the class is loaded, but it is just a guess. Does anybody here know for sure?

+1  A: 

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?

eed3si9n
+4  A: 

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.

Jon Skeet
By the way, you forgot to capitalize String twice in the first line of code.
R. Bemrose
Thanks - fixed now. Too used to C#.
Jon Skeet
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...
Rogerio
@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.
Jon Skeet
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.
Rogerio
@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.
Jon Skeet
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#.
Rogerio
+1  A: 

Type erasure occurs at compile time. What type erasure means is that it will forget about the generic type, not about every type. Besides, there will still be metadata about the types being generic. For example

Box<String> b = new Box<String>();
String x = b.getDefault();

is converted to

Box b = new Box();
String x = (String) b.getDefault();

at compile time. You may get warnings not because the compiler knows about what type is the generic of, but on the contrary, because it doesn't know enough so it cannot guarantee type safety.

Additionally, the compiler does retain the type information about the parameters on a method call, which you can retrieve via reflection.

This guide is the best I've found on the subject.

Vinko Vrsalovic
+5  A: 

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.

erickson
A: 

For all Java Generics related questions, I highly recommend "Java Generics and Collections" by Maurice Naftalin, Philip Wadler.

Julien Chastang