Fiary
Fiary

Reputation: 203

java generics type erasure

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

Answers (2)

Wyzard
Wyzard

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

Jon Skeet
Jon Skeet

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

Related Questions