在覆盖上,如果仅涉及原始/未基于的类型,则不需要其他类型变量来缩小超级方法的返回类型。示例:在继承上缩小返回类型(涉及仿制药)
但是,如果类型参数被缩小 - 虽然原始类型保持不变 - 则需要附加的方法类型变量。
示例:
import java.util.List;
public class InheritanceTest {
public interface A<T> {
A<T> test();
List<A<T>> testList1();
<U extends A<T>> List<U> testList2();
<U> List<A<T>> testList3();
// extending class/interface does not need to suppress warnings:
List<? extends A<T>> testList4();
}
public interface B extends A<Integer> {
@Override
B test();
@Override
List<B> testList1();
@Override @SuppressWarnings("unchecked")
List<B> testList2();
@Override @SuppressWarnings("unchecked")
List<B> testList3();
@Override
List<B> testList4();
}
}
编译器抱怨testList1()
的不兼容返回类型,但没有报告其余三种方法的任何错误。
testList3()
中未使用的变量如何使编译器关闭?
当您用协变返回类型的方法覆盖方法时,您实际上是在说覆盖返回类型是超级返回类型的子类。表示List<B>
可分配给List<A<T>>
,这是不正确的。这是一个简单的示例,没有方法声明使用您的类型(A<T>
和B
;他们在这里无关紧要的方法):
List<A<Integer>> listOfA;
List<B> listOfB = new ArrayList<>();
listOfA = listOfB; // compile error: incompatible types: List<B> cannot be converted to List<A<Integer>>
List<? extends A<Integer>> listOfAFixed;
listOfAFixed = listOfB;
...以及不允许此任务的原因,因为可能会有一个C extends A<Integer>
引入了ClasscastException的潜力
List<C> listOfC = new ArrayList<>();
listOfA = listOfC; // let's assume this compiles
B b = getAB();
listOfA.add(b); // adding to listOfC, right?
C c = listOfC.get(0); // whops, ClassCastException
您可以用Number
,Integer
替换A<T>
,用CC_11和C
替换Float
;同样的事情也会发生。问题不是在A
上的仿制药,而是与List
的通用物。
使用相同的逻辑,我也尝试在方法主体中重现其他协变量覆盖(〜分配),但它们也没有发出警告,而是错误。这很奇怪。
要意识到的一件事是,从A
中删除参数仍然给出相同的警告:
public interface A {
<U extends A> List<U> testList2();
<U> List<A> testList3();
}
public interface B extends A {
@Override List<B> testList2();
@Override List<B> testList3();
}
我认为诀窍在于,覆盖方法不够"通用"(我知道缺乏更好的术语),请查看javap
列出的内部签名:
<U::LInheritanceTest$A;>()Ljava/util/List<TU;>;
()Ljava/util/List<LInheritanceTest$B;>;
<U:Ljava/lang/Object;>()Ljava/util/List<LInheritanceTest$A;>;
()Ljava/util/List<LInheritanceTest$B;>;
请注意,覆盖方法缺乏<>
,我想这类似于使用原始类型,因此未检查的警告而不是错误:它使用部分删除的类型。
这是我放弃的点,也许安迪或杰恩可以更正式的解释。
在原始类型和通用类型混合的情况下,语言规格放弃了。否则将引入更加晦涩的规则,所有这些都支持应避免的情况。
A.testList3
是一种通用方法。B
试图声明与非生成相同的方法(相同的名称,与参数相同的类型和返回)。因此,似乎语言规格忽略了所有兼容方法的仿制药。
(所以注意,不要抑制,警告。)