繁体   English   中英

我可以在列表声明中使用泛型通配符吗?

[英]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的属性。

由于SubSuper的子类,因此任何仅包含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.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM