Question

Consider this example:

import java.util.*;

class Foo<T> {
  public int baz(List<String> stringlist) { return 1; }
  public int baz(ArrayList<Object> objectlist) { return 2; }

  public static void main(String[] args) {
    Foo<String> foo = new Foo<String>(); // (A)
    //Foo foo = new Foo();               // (B)

    System.out.println(foo.baz(new ArrayList<String>()));
  }
}

Why does it print 1 in (A), but 2 with (B)?

I know how method resolution works, so no need to explain that to me.

I want to know the deeper motivation behind this "feature". Why is there no erasure warning about it? (There is just one about Foo foo = new Foo().)

Why does method resolution use erased semantics although the generic type is given?

Was it helpful?

Solution

It's because when the compiler is resolving overloads, it considers each method as either generic or non-generic, never a mixture of the two, so the candidates are:

  1. Foo<T>.baz(List<String>)
  2. Foo<T>.baz(ArrayList<Object>)

if foo is a Foo<String>, or

  1. Foo.baz(List)
  2. Foo.baz(ArrayList)

if foo is a Foo.

There is no such thing as Foo.baz(List<String>). Either all type parameters are considered or none are. (I am not aware of this being explicitly stated in the JLS, but it makes sense since treating a generic method as if it were the raw equivalent is a backwards-compatibility feature.)

In the first case, Foo<T>.baz(List<String>) matches but Foo<T>.baz(ArrayList<Object>) does not.

In the second case both functions match, and Foo.baz(ArrayList) is more specific, so it is chosen.

OTHER TIPS

It won't even compile case (A) if you only keep the single method baz(ArrayList<Object>) (error: method cannot be applied to given type). I guess ArrayList<String> is not a subclass of ArrayList<Object>.

It's weird that it does compile in case (B), but they must have taken some weird decisions to kept backward compatibility with non-generic collections.

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top