The code below doesn't compile on OpenjDK 11. It seems to me that test1 in B should override test1 in A, because:
- The methods have same name.
- The methods have same parameter list.
- The methods have same visibility.
- The methods actually do not throw potentially incompatible checked exceptions.
- Their return types are covariant.
I took a decompiler and decompiled each class separately. The compiled code actually worked as I would expect. It replaced U extends Number with Number, T extends Integer with Integer and so on. But when I try to compile the two classes together, I got and error on the second class that says the test in the second class does not override a method in the first one.
I'm missing something big or small here. And it's probably related to 5. Maybe the types are not covariant. Can you help me?
class A {
<U extends Number, T extends Number> U test(T test) {
System.out.println("In A.test(T test)");
return null;
}
//Decompiler shows that the above method erases to
// Number test(Number test)
// Just like the method below.
Number test2(Number test) {
return null;
}
}
class B extends A {
//Unsuccessful override. Compiler error.
@Override
<U extends Integer, T extends Number> U test(T test) {
System.out.println("In B.test(T tesT)");
return null;
}
//Decompiler shows that the above method erases to
//Integer test(Number test)
//Just like the method bellow.
//Successful override
@Override
Integer test2(Number test) {
return null;
}
}
CodePudding user response:
The reason why your test methods produce an error is because they have different signatures. Note that the signature of a method consists of its name, parameter list, and type parameters.
Quote from the Java Language Specification:
Two methods or constructors, M and N, have the same signature if they have the same name, the same type parameters (if any) (§8.4.4), and, after adapting the formal parameter types of N to the type parameters of M, the same formal parameter types.
Crucially, your two test methods do not have the same type parameters, because U in A.test has a different bound from U in B.test.
Two methods or constructors M and N have the same type parameters if both of the following are true:
M and N have same number of type parameters (possibly zero).
Where A1, ..., An are the type parameters of M and B1, ..., Bn are the type parameters of N, let θ=[B1:=A1, ..., Bn:=An]. Then, for all i (1 ≤ i ≤ n), the bound of Ai is the same type as θ applied to the bound of Bi.
Think about what would happen if B.test actually overrode A.test. You could pass a type to the type parameter U that is out of its bounds!
A a = new B();
// This will call B.test, and U is Double, T is Integer
// but U should extends Integer!
Double x = a.test((Integer)0);
