我正在阅读一篇关于java中协方差的有趣的dzone文章,这很容易理解,但有一件事让我觉得不合理,文章就在这里https://dzone.com/articles/covariance-and-contravariance
我引用了这篇文章的例子,它解释了为什么不能将一个集合添加到:
使用协方差,我们可以从结构中读取项目,但我们不能在其中写入任何内容。所有这些都是有效的协变声明。
List<? extends Number> myNums = new ArrayList<Integer>();
因为我们可以确定无论实际列表包含什么,它都可以被上传到一个数字(毕竟任何扩展数字的数字都是数字,对吧?) 但是,我们不允许将任何内容放入协变结构中。
myNumst.add(45); //compiler error
这是不允许的,因为编译器无法确定通用结构中对象的实际类型。它可以是扩展Number的任何东西(如Integer,Double,Long),但编译器无法确定
上面的段落对我来说没有意义,编译器知道列表包含Number
或任何扩展它的内容,ArrayList
被输入Integer。编译器知道插入的文字int。
那么为什么它会强制执行,因为在我看来它可以确定类型?
答案 0 :(得分:5)
当编译器出现时:
List<? extendsNumber> myNums = new ArrayList<Integer>();
它会检查左侧和右侧的类型是否合在一起。 但编译器不&#34;记得&#34;用于右侧实例化的特定类型。
可以说编译器会记住这个myNums
:
List<? extendsNumber>
是编译器可以不断折叠;和数据流分析;并且可能可以让编译器跟踪&#34;实例化类型&#34;信息 - 但唉:java编译器不这样做。
它只知道myNums
已初始化List<? extends Numbers>
- 仅此而已。
答案 1 :(得分:4)
你缺少两点:
您只考虑局部变量:
public void myMethod() {
List<? extends Number> list = new ArrayList<Integer>();
list.add(25);
}
编译器可以很容易地检测到?
的实际值,但我知道没有人会编写这样的代码;如果您要使用整数列表,则只需将变量声明为List<Integer>
。
协方差和逆变在处理参数和/或结果时最有用;这个例子更现实:
public List<Integer> convert(List<? extends Number> source) {
List<Integer> target = new ArrayList<>();
for (Number number : source) {
target.add(number.intValue());
}
return target;
}
编译器如何知道哪个类型用于参数化列表?即使在编译时,所有调用都只传递ArrayList<Integer>
的实例,稍后代码可以使用带有不同参数的方法而不重新编译类。
上面的段落对我来说没有意义,编译器知道列表包含Number或任何扩展它的东西,并且ArrayList被键入Integer。编译器知道插入的文字int。
不,编译器知道的是列表包含扩展Number
的某些(包括Number
)。它无法判断它是List<Number>
(在这种情况下您可以插入Number
的任何实例)或List<Integer>
(在这种情况下,您只能插入Integer
个实例) 。但它知道您使用get
检索的所有内容都是Number
的实例(即使它不确定具体的类)。
答案 2 :(得分:4)
上面的段落对我来说没有意义,编译器知道列表包含Number或任何扩展它的东西,并且ArrayList被键入Integer。编译器知道插入的文字int。
考虑一个略有不同但相关的例子:
Object obj = "";
通过上面的论证,编译器也应该能够知道obj
实际上是String
,因此您可以调用String
- 具体方法在它上面:
obj.substring(0);
任何有一点Java经验的人都知道你不能。
您(以及仅您)(或者,至少是编写代码的人)具有类型信息,并且已经做出了故意做出的丢弃决定它:没有理由将变量类型声明为Object
,那么为什么编译器必须投入工作来尝试恢复该信息呢? (*)
出于同样的原因,如果您希望编译器知道变量的值
List<? extends Number> myNums = new ArrayList<Integer>();
是ArrayList<Integer>
,声明该变量属于该类型。否则,对于编译器而言,它更简单地假设&#34;这绝对是类型范围内的任何内容&#34;。
(*)我在SO的某个地方的某个地方读到了这个论点。我不记得它在哪里,或者我给出了适当的归属。