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.
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.
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.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With