7

在下面的 Java 代码中,我创建了一个 list 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

我已经通过这个链接。我无法得到确切的理由。

4

4 回答 4

6

是否可以在该列表中添加新项目?

不...因为该代码不知道它“实际上”是什么列表。想象一下,如果你可以

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 获取值......而当您使用通配符时? super T您只能通过 API 输入值 - 因为这是安全的。

于 2015-09-16T16:57:17.427 回答
2

不,它不是只读的......即使这通常是意图。

给定一个List<? extends Number>对象,编译器其类型转换为List<X>其中 X 是 Number 的未知子类型。因此,对象确实有add(X)方法。我们可以使用X参数调用该方法……例如,null.

并且由于get()返回X,我们也可以add()使用来自 ... 的值进行调用。get()直接调用list.add(list.get(i))是行不通的,即使它是有意义的。我们需要一个小帮手

经典的例子是Collections.reverse(List<? extends Object> list)。此方法将修改list, 尽管有通配符。

clear()当然,您也可以在任何列表上调用变异方法,例如。


话虽这么说,通配符确实主要用于使用站点的变化,并且大多数情况下,它传达了 API 设计者的意图,即类型参数是用于in还是out。例如,通过声明List<? super/extends Foo>,API 表示它打算将 T 注入到列表中,或者 从列表中取出 T。

通配符使只读/只写是一种误解。但这种误解适用于大多数用例。越多的人有这种误解,它就越会成为一种惯例……

请参阅我关于通配符的文章 - http://bayou.io/draft/Capturing_Wildcards.html

于 2015-09-16T17:07:50.750 回答
0

当您将其List<? extends Number> nums视为List某种类型的事物时,它会有所帮助 extends Number,但您不能确定是什么。因此,您所做的一切都nums需要能够完成这样的事情。

添加null作品,因为null可以转换成任何东西。您尝试添加的所有其他内容都会失败,因为编译器无法 100% 确定您添加的内容扩展了列表的组成部分。

而不是List<? extends Number> numsdo List<Number> nums,因为您仍然可以放入任何可扩展的内容Number

?并不意味着“任何东西”,它更接近于“一些特定但未知的”。

于 2015-09-16T17:41:52.340 回答
-1

泛型只是编译时。

所以编译器将决定我们要使用的实际类型。

List<? extends Number>

这意味着我们不确定对象的实际类型。

所以编译器不确定列表的实际类型是什么。

于 2015-09-16T16:57:49.103 回答