Reputation: 203
When declaring a class with type parameter
<T extends A & B & C>
,type erasure process will replace T
with type A
.
But if variable of type T
calls method declaring in interface B
or C
, what does Java do with it?
And when we create an instance of generic type such as ArrayList<String>
, the type parameter String
will also be erased, but calling get
method will return type
String
, where does this information come from since it has been erased ?
I know java reflection is used, but I need some specific explanation.
Upvotes: 3
Views: 443
Reputation: 34563
Type erasure effectively replaces all the generic type parameters with Object
after the compiler has done its type checking. In your <T extends A & B & C>
example, A
is erased just like everything else.
When you call get
on an ArrayList<String>
, the compiler produces bytecode that casts the returned object to a string automatically. The list itself doesn't "know" that it's a list of strings (due to the type erasure), but the code that calls get
knows that it expects the thing gotten from the list to be a string.
Upvotes: 2
Reputation: 1499760
Reflection isn't used - casting is. For example, take this code:
interface A {
void a();
}
interface B {
void b();
}
interface C {
void c();
}
class Generic<T extends A & B & C> {
T t;
Generic(T t) {
this.t = t;
}
void callMethods() {
t.a();
t.b();
t.c();
}
}
Now look at the bytecode for Generic
(constructor removed):
class Generic extends java.lang.Object{
A t;
void callMethods();
Code:
0: aload_0
1: getfield #2; //Field t:LA;
4: invokeinterface #3, 1; //InterfaceMethod A.a:()V
9: aload_0
10: getfield #2; //Field t:LA;
13: checkcast #4; //class B
16: invokeinterface #5, 1; //InterfaceMethod B.b:()V
21: aload_0
22: getfield #2; //Field t:LA;
25: checkcast #6; //class C
28: invokeinterface #7, 1; //InterfaceMethod C.c:()V
33: return
}
Note the checkcast
instructions before each of the invokeinterface
calls to b()
and c()
.
The result is exactly as if Generic
were actually written like this:
class Generic<T extends A> {
T t;
Generic(T t) {
this.t = t;
}
void callMethods() {
t.a();
((B) t).b();
((C) t).c();
}
}
As for your question about ArrayList
- the information about the return type of get()
being the element type of the list is still stored as part of the ArrayList
class. The compiler will again insert casts in the calling code, so:
ArrayList<String> strings = new ArrayList<String>();
strings.add("foo");
String x = strings.get(0);
is equivalent at execution time to:
ArrayList strings = new ArrayList();
strings.add("foo");
String x = (String) strings.get(0);
One important aspect of this is that you can't ask an ArrayList
object at execution time what T
is - that information has been erased.
Upvotes: 6