以下代码无法在 OpenjDK 11 上编译。 在我看来,B 中的 test1 应该覆盖 A 中的 test1,因为:
- 这些方法具有相同的名称。
- 这些方法具有相同的参数列表。
- 这些方法具有相同的可见性。
- 这些方法实际上不会抛出可能不兼容的已检查异常。
- 它们的返回类型是协变的。
我拿了一个反编译器,分别反编译了每个类。编译后的代码实际上如我所料地工作。它将 U extends Number 替换为 Number,T extends Integer 替换为 Integer 等等。但是当我尝试将这两个类编译在一起时,我在第二个类上遇到错误,表明第二个类中的测试没有覆盖第一个类中的方法。
我在这里遗漏了一些或大或小的东西。它可能与 5 有关。也许类型不是协变的。你能帮帮我吗?
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;
}
}
最佳答案
您的 test
方法产生错误的原因是它们具有完全不相关的不同签名。请注意 signature一个方法由它的名称、参数列表、和类型参数组成。
引自 Java 语言规范:
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.
至关重要的是,您的两个 test
方法没有 same type parameters ,因为 A.test
中的 U
与 B.test中的
.U
有不同的 bound
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.
想想如果 B.test
实际上覆盖了 A.test
会发生什么。您可以将类型传递给超出其范围的类型参数 U
!
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);
欲了解更多信息,here是何时发生覆盖的精确规则。请注意,实际上并未考虑您列表中的条件#4 和#5。它们只是额外的要求,如果您破坏它们,它们会使您的代码无法编译。即使您违反了这些要求,一种方法仍被“定义”为覆盖另一种方法。它们被列为 here在 JLS 中。
关于java - 为什么在这种情况下删除通用类型会阻止覆盖?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/70607154/