![](/img/trans.png)
[英]Java Generics WildCard - How can I use generic with Lambda here?
[英]Can I use generics wildcard in List declaration?
考虑以下代码:
class Super {}
class Sub extends Super {}
class Test {
public static void main(String[] args) {
List<? extends Super> list = new ArrayList<Sub>(); //1
list.add(new Sub()); //2
}
}
第1行编译成功,但第2行编译失败:
The method add(capture#2-of ? extends Super) in the type List<capture#2-of ? extends Super> is not applicable for the arguments (Sub)
我的问题是:
1)为什么第1行成功编译?
2)第1行是声明列表(或其他集合)的一种良好做法吗?
3)为什么在第1行中将list声明为Sub类型后,第2行为什么编译失败?
4)Eclipse的自动完成功能说,列表中现在仅允许“ null”元素。 为什么?
非常感谢!
1)为什么第1行成功编译?
第一行进行编译,因为List<Sub>
是List<? extends Super>
子类List<? extends Super>
List<? extends Super>
,并且只有在List不允许您向其添加任何新成员的情况下才可以。
?
意味着您并不完全知道它是List<Sub>
或List<Sub1>
,因此允许将新元素添加到列表中是不安全的,因此也不允许这样做。
2)第1行是声明列表(或其他集合)的一种良好做法吗?
如果您已经知道它将成为List<Sub>
那么我找不到任何用处,但是当您将List传递给其他类(如Utilities)时,会使用通配符。
3)为什么在第1行中将list声明为Sub类型后,第2行为什么编译失败?
因为正如我已经解释的那样,当您不知道确切类型时,将任何元素添加到列表都是不安全的。
4)Eclipse的自动完成功能说,列表中现在仅允许“ null”元素。 为什么?
因为null
是每种引用类型,所以这就是为什么您可以为任何对象分配null
值的原因。
使用泛型时,请务必记住Josh Bloch的PECS(生产者扩展消费者超级用户)规则
好的参考文献:
如第1行所示,捕获声明在方法参数中很有用。 请参见Collection.addAll(Collection<? extends E>)
。 如果您需要带有扩展Super
的列表,只需使用List<Super>
。
您已将list
声明为,可以将其分配给Super
类的任何子类型。 因此,分配一个Sub
type列表就可以了,Compiler允许进行编译。 但这并不意味着您可以在其中添加特定的对象类型。
<? extends Super>
<? extends Super>
并非意味着,您可以添加Super的任何子类型。 这意味着,您可以为其分配任何子类型集合。
1)为什么第1行成功编译?
您基本上将列表定义为包含扩展(或为Super
)的任何类型的元素,即,编译器知道该列表中的每个元素至少应具有Super
的属性。
由于Sub
是Super
的子类,因此任何仅包含Sub
元素的列表也满足所有元素都是Super
实例的要求, List<? extends Super> list = new ArrayList<Sub>();
List<? extends Super> list = new ArrayList<Sub>();
是正确的。
2)第1行是声明列表(或其他集合)的一种良好做法吗?
作为取决于个人风格的局部变量,恕我直言。 以这种方式声明参数(或实例/静态变量)时,通常不仅是好的样式,而且也是必需的。
考虑一种迭代数字集合并返回总和的方法。 您可以将参数声明为Collection<Number>
但是如果没有讨厌的转换,就无法传递Collection<Integer>
。 如果参数声明为Collection<? extends Number>
Collection<? extends Number>
您可以传递Collection<Integer>
。
3)为什么在第1行中将list声明为Sub类型后,第2行为什么编译失败?
原因是编译器不知道列表中元素的确切类型。 是Super
列表还是Sub
列表?
以List<? extends Number> list
为例 List<? extends Number> list
。 您不知道您是否具有List<Number>
, List<Double>
或List<Integer>
,因此无法确定list.add( new Integer(1) );
会没事的。 编译器就是这样看的。
4)Eclipse的自动完成功能说,列表中现在仅允许“ null”元素。 为什么?
我不得不在这里猜测,但是将null
添加到列表中就可以了,因为无论实际列表声明的是哪种类型,您都可以始终将null
转换为该类型。
1)为什么第1行成功编译?
您将list
声明为“源自Super
的事物的列表”。 您为其分配一个Sub
列表。 Sub
是“源自Super
东西”。
2)第1行是声明列表(或其他集合)的一种良好做法吗?
不可以。通配符用于功能参数。 局部变量应在其泛型参数中尽可能具体,以避免出现您所面临的问题。
3)为什么在第1行中将list声明为Sub类型后,第2行为什么编译失败?
谬论。 list
被声明为具有“源自Super
元素”的元素,而不是您声称的Sub
。 而且您不能将Sub
添加到列表中,因为“东西”可能不是Sub
; 它可能是Sub2
,并且添加等同于此非法分配:
class Super {}
class Sub extends Super {}
class Sub2 extends Super {}
Sub2 s = new Sub();
此处的主要误解似乎是您认为通配符会在赋值时被替换。 不是。 它仍然是通配符,并且仅进行兼容性检查。
4)Eclipse的自动完成功能说,列表中现在仅允许“ null”元素。 为什么?
null
是唯一具有任何可能引用类型的值,因此无论通配符代表什么,都与列表兼容。
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.