Reputation: 983
As far as I understand, Java Generics erases all the information concerning the parameter type T in the generic method (or in the generic class). That's why we can't use
new expression, such as
new T()
instanceof expression, such as
if(obj instanceof T)
inside a generic method.
My question is how the parameter type T works inside a generic method when it comes to casting. For example, I have 3 simple classes here:
public class People {
String name;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public People(String name) {
super();
this.name = name;
}
@Override
public String toString() {
return "People [name=" + name + "]";
}
}
public class GenericClass<T> {
public T getCast(People p) {
return (T)p;
}
}
public class App
{
public static void main( String[] args )
{
People p = new People("Kewei");
GenericClass<Integer> genericClass = new GenericClass<Integer>();
Object p_object = genericClass.getCast(p);
System.out.println(p_object);
}
}
When it executes the (T)p
in the generic method getCast(People p)
. Does it just take the type T as Object? or It just removes the whole (T)
casting at compile time? I've read Bruce Eckel's «Thinking in Java» from which I roughly understand as:
Java compiler checks at method's entry/leave points at compile time to make sure the internal consistency of generic method, and inserts casting code (at compile time).
Is it the right understanding?
Thanks.
Upvotes: 4
Views: 330
Reputation: 122429
You can mentally think of the compiler doing a "re-writing" of the code with generics into code without generics.
In the generic class, T gets erased to the erasure of its upper bound, which in this case is Object, but could be something else. The cast to T becomes a cast to T's upper bound (which in this case is Object, so it is redundant and the compiler might skip it). The method similarly returns T's upper bound (which is Object). So there is no runtime check that the cast is valid inside the method.
public class GenericClass {
public Object getCast(People p) {
// In this case the cast to T is removed because T's upper bound is Object.
// But if T's upper bound was, say, Number, then there would still be a cast
// to the upper bound. e.g. "(Number)p", and the return type would also be Number
return p;
}
}
In the places that call the method that returns T, the compiler will insert a cast into the actual type of T. In this case, in the main method, you have a GenericClass<Integer>
, so when getCast
returns T, it is cast to Integer. So in this case the runtime check now gets "moved" to the calling method, not the method with the cast.
// somewhere else
GenericClass genericClass = new GenericClass();
p_object = (Integer)genericClass.getCast(p);
It is also possible that the caller kept a GenericClass<S>
reference (for some type parameter S in its scope), so that the result is cast to S, not a specific type. Then we would repeat the whole re-writing process again (i.e. the cast to S is re-written to a cast to its upper bound, and places that call into that method will cast the result to their actual type argument). This means that the runtime check might be deferred to yet another place, and so on.
Upvotes: 4
Reputation: 9916
Java never casts anything; at run time it's simply treated "as an object". The JRE attempts to call methods on that object, and since they've been proven to exists at compile it, it works.
There's no reference to what class T is at runtime. None. So no casting is done.
Upvotes: -1