EL_9
EL_9

Reputation: 414

Type erasure with collections

I am having trouble understanding how type erasure exactly works when collections are involved.

If we look at this code:

static void print(Object o) {
   System.out.println("Object");
}
static void print(String s) {
   System.out.println("String");
}
static void print(Integer i) {
   System.out.println("Integer");
}
static <T> void printWithClass (T t) {
   print(t);
}
public static void main(String[] args) {
   printWithClass("abc"); // Object is printed, because T turns to Object after type erasure.
}

This example I understand, however this one I could not:

class Printer<T>{
    void print(Collection<?> c){
        System.out.println("1");
    }
    void print(List<Number> l){
        System.out.println("2");
    }
    void printWithClass(List<T> ts){
        print(ts);
    }
    
}

public class Main
{
 public static void main(String[] args){
        Printer<Integer> printer=new Printer<>();
        printer.printWithClass(new ArrayList< Integer>()); //1 is printed
    }  
}

I am unable to understand why exactly 1 is printed. It seemed to me that after type erasure the code will look like this:

        void print(Collection c){
            System.out.println("1");
        }
        void print(List l){
            System.out.println("2");
        }
        void printWithClass(List ts){
            print(ts);
        }

But this is wrong.

It seems to me as if there are two phases of type erasure - one that will turn T to Object, and then one where List<...> will be turned into List.

I don't think it's correct, can someone please explain what is happening here?

Edit: I came up with this guess: The compiler remembers that originally the parameter was List<Number> because it's important for separate compilation. However, it cannot remember that what came in originally to printWithClass was List<Number> as well, it can only know it was List<?>.

Upvotes: 3

Views: 300

Answers (1)

Joachim Sauer
Joachim Sauer

Reputation: 308001

Remember that method overloading is resolved at compile time (contrary to method overriding which will be resolved at runtime).

This means that the decision which specific method signature is being called will have to be done when printWithClass is compiled. Which also means that no type erasure is done at this point (since erasure is a property of the runtime, the compiler still cares about all the type parameters).

In this method:

void printWithClass(List<T> ts){
    print(ts);
}

with T being fully unbounded (i.e. it could be any reference type possible) there is only one possible print method to call, which is the one taking a Collection<?>, because we can't know that T is Number.

As a side-note, since a List<Integer> is not a List<Number>, even if these rules were different, your main method couldn't cause the other print method to be called.

Upvotes: 4

Related Questions