在下面的代码Java中,我创建了一个列表nums
。我可以在声明期间分配另一个列表。但是除了null
之外,无法添加新项目。那么,这是否意味着nums
是只读的? 为什么?是否可以在该列表中添加新项目?
List<Integer> ints = new ArrayList<Integer>();
ints.add(1);
ints.add(2);
List<? extends Number> nums = ints;
nums.add(3.14); //Generates error
nums.addAll(ints); //Generates error
nums.add(null); //works
System.out.println(nums.get(0)); //works
我已经完成了这个link。我无法得到确切的理由。
答案 0 :(得分:6)
是否可以在该列表中添加新项目?
没有......因为该代码并不知道它的实际情况&#34;的列表。想象一下,如果你可以:
List<String> strings = new ArrayList<>();
List<? extends Object> objects = strings; // This is fine
objects.add(new Object()); // Fortunately this *isn't* valid...
System.out.println(strings.get(0).length()); // Or what would this do?
基本上,当您使用像? extends T
这样的通配符时,您只能通过API获取值 out ...当您使用? super T
之类的通配符时,您可以只能通过API将值放在中 - 因为这样做是安全的。
答案 1 :(得分:2)
不,它不是只读的......即使这通常是意图。
给定一个List<? extends Number>
对象,编译器converts的类型为List<X>
,其中X是Number的未知子类型。因此,该对象确实具有add(X)
方法。我们可以使用X
参数调用该方法...例如,null
。
由于get()
返回X
,我们也可以使用add()
中的值来调用get()
....直接调用list.add(list.get(i))
将无法正常工作,即使它有意义。我们需要一点helper。
经典的例子是Collections.reverse(List<? extends Object> list)
。尽管使用了通配符,此方法仍将修改list
。
当然,您也可以在任何列表中调用clear()
等变异方法。
话虽如此,通配符确实主要用于使用网站variance,并且大多数情况下,它传达了API设计者的意图,即类型参数是否适用于 或者 out 。例如,通过声明List<? super/extends Foo>
,API表示它打算将T插入,或者从中获取T,列表。
通配符使读/写成为一种误解。但是这种误解在大多数用例中起作用。这种误解的人越多,它就越成为一种惯例......
请参阅我关于通配符的文章 - http://bayou.io/draft/Capturing_Wildcards.html
答案 2 :(得分:0)
当您将List<? extends Number> nums
视为List
延伸Number
的某种类型的事物时,它会有所帮助,但您无法确定是什么。因此,您使用nums
所做的一切都需要能够做到这一点。
添加null
有效,因为null
可以投射到任何内容中。您尝试添加的其他所有内容都将失败,因为编译器无法100%确定您添加的内容会扩展列表所构成的内容。
而不是List<? extends Number> nums
执行List<Number> nums
,因为您仍然可以放入任何扩展Number
的内容。
?
并不意味着&#34;任何事情&#34;,它更接近于意义&#34;某些具体但未知的&#34;。
答案 3 :(得分:-1)
泛型只是编译时间。
因此编译器将决定我们将要使用的实际类型。
List<? extends Number>
这意味着我们不确定该对象的实际类型。
因此编译器无法确定列表的实际类型是什么。