在重写时,如果仅限于原始/未删除类型,则不需要额外的类型变量来缩小super方法的返回类型。示例:Narrowing return types on inheritance (generics involved)
但是如果类型参数变窄 - 原始类型保持不变 - 则需要一个额外的方法类型变量。
示例:
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()
中未使用的变量如何使编译器闭嘴?
答案 0 :(得分:1)
当你用一个协变返回类型覆盖一个方法时,你实际上是说重写的返回类型是超级返回类型的子类。含义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
您可以将A<T>
替换为Number
,将B
替换为Integer
,将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();
}
我认为诀窍在于最重要的方法不够通用&#34; (我知道缺乏更好的术语),请查看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;>;
请注意,重写方法缺少<>
,我猜这类似于使用原始类型,因此未经检查的警告而不是错误:它使用部分擦除类型。
这是我放弃的地方,也许Andy或Jorn可以更正式地解释这一点。
答案 1 :(得分:0)
在原始类型和泛型类型混合的情况下,语言规范放弃了。否则会引入更加模糊的规则,以支持应该避免的情况。
A.testList3
是一种通用方法。 B
尝试声明与非泛型相同的方法(相同名称,与参数相同的已擦除类型并返回)。因此,似乎语言规范忽略了使方法兼容的所有泛型。
(注意,不要压制,警告。)