Why do raw types in one place cause generic callsites somewhere else to be treated as raw?
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开发者_JAVA技巧 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?
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:
Foo<T>.baz(List<String>)
Foo<T>.baz(ArrayList<Object>)
if foo
is a Foo<String>
, or
Foo.baz(List)
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.
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.
精彩评论