Ran into an interesting issue; the following class compiles:
public class Test {
public static void main(String[] args) throws Exception {
A a = new A();
B b = new B();
foo(a);
foo(b);
}
private static void foo(A a) {
System.out.println("In A");
}
private static void foo(B b) {
System.out.println("In B");
}
private static class A {}
private static class B extends A {}
}
but this one fails:
public class Test {
public static void main(String[] args) throws Exception {
A<String> a = new A<>();
B b = new B();
foo(a);
foo(b);
}
private static void foo(A<String> a) {
System.out.println("In A");
}
private static void foo(B b) {
System.out.println("In B");
}
private static class A<T> {}
private static class B extends A {}
}
with this error:
Test.java:8: error: reference to foo is ambiguous, both method foo(A<String>) in Test and method foo(B) in Test match
foo(b);
^
Note: Test.java uses unchecked or unsafe operations.
Note: Recompile with -Xlint:unchecked for details.
1 error
I'd have thought that due to type erasure that these would be essentially identical. Any one know what's going on here?
A method in a generic class can be overridden like any other method. The output is shown here: The overridden version of getValue() is called for objects of type MyClass2, but the superclass version is called for objects of type MyClass.
Generic methods are methods that introduce their own type parameters. This is similar to declaring a generic type, but the type parameter's scope is limited to the method where it is declared. Static and non-static generic methods are allowed, as well as generic class constructors.
According to oracle documentation, the following points are the disadvantage of generics: Cannot instantiate Generic types with primitive types. Cannot create instances of type parameters. Cannot declare static fields whose types are type parameters.
The cause is that you are mixing generics and raw types (B should be declared like class B<T> extends A<T>
or class B extends A<SomeType>
).
The actual reason why this is happening is buried somewhere in the JLS, section #15.12.2.7 and following - good luck to articulate it succintly ;-)
Before the dawn of generics, Java had methods like
public class Collections
public void sort(List list) {...} [1]
And user code may have things like
public class MyList implements List ... [2]
MyList myList = ...;
Collections.sort(myList); [3]
When generics was added to Java, a decision was made to convert existing classes and methods to generic ones, without breaking any code using them. That is a great achievement in terms of difficulty, at the great price of leaving the language complicated and flawed.
So [1]
was generified, but [3]
must still compile as is, without having to generify [2]
.
The hack is in §15.12.2.3
Ai can be converted by method invocation conversion (§5.3) to Si
basically saying that if the argument type (Ai) is raw, then erase the parameter type (Si) too for the purpose of matching.
Back to your example, we see why foo(A<String>)
is considered applicable for foo(b)
.
However there's another question - is foo(A<String>)
applicable per [§15.12.2.2]? The answer seems to be "no" by the letter of the spec. But it may be a bug of the spec.
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