未绑定通配符与原始类型之间的差异

我正在阅读关于generics的内容,我不明白是否需要未绑定的通配符以及它与原始类型的区别。 我读了这个问题,但仍然没有弄清楚。 在未绑定的通配符的Java教程页面中,我得到了以下两点,我没有理解第一点:

  • 如果您正在编写可以使用Object类中提供的function实现的方法。
  • 当代码使用generics类中不依赖于类型参数的方法时。 例如, List.size()List.clear() 。 实际上,经常使用Class因为Class大多数方法都不依赖于T

有人可以用外行语言解释未绑定的通配符和原始类型之间的区别。

ListList有何不同?

ListList不同之处

主要区别在于第一行编译但第二行不编译:

 List list = new ArrayList (); List list = new ArrayList (); 

但是,因为您不知道List的generics类型是什么,所以您不能使用其参数化方法:

 List list = new ArrayList (); list.add("aString"); //does not compile - we don't know it is a List list.clear(); //this is fine, does not depend on the generic parameter type 

至于与原始类型(没有generics)的区别,下面的代码编译并运行正常:

 List list = new ArrayList (); list.add("aString"); list.add(10); 

ListList不同之处是什么?

  List l1 = new ArrayList(); List l2 = new ArrayList(); l1.add("Object"); //l2.add("Object"); incorrect l2.add(null); 

您只能将空值添加到List

就个人而言,我从通配符的Java教程中发现这个额外的链接很有用。

我在ListList之间看到的主要区别之一是前者只能用于读取它的元素(除非你真的想要添加null ),后者允许(未经检查)添加任意类型的对象它可能带来意想不到的副作用。

List在方法签名中很有用,可以调用从不需要type参数的方法,例如,从列表中读取或旋转它。

 void someMethod(List list) { list.clear(); // I will never add anything to the list in here } 

您永远不会添加任何内容或以其他方式修改列表,因为您不能向列表中添加任何内容,除非在具有此签名的方法中为null,因此您不会破坏类型安全性。 另一方面,您可以对原始列表执行任何操作,我们都知道这会导致类型安全违规。

 void someMethod2(List list) { list.add(new WeaselFurBrush()); } List list1 = new ArrayList(); someMethod2(list1);// oops 

有人可以用外行语言解释未绑定的通配符和原始类型之间的区别。

未绑定的通配符类型可以维护集合的类型不变 ,而原始类型不能。 正如Joshua Bloch在他的Effective Java中所说,

您可以将任何元素放入具有原始类型的集合中,轻松破坏集合的类型不变量(如第112页的unsafeAdd方法所示); 你不能把任何元素(除了null)放入Collection <?>中。

因此,只要将参数化类型列表分配给未绑定通配符类型列表,就会保留集合的类型不变量

 List list1 = new ArrayList(); list1.add("foo"); list1.add("bar"); List list2 = list1; 

如果指定其元素具有不同类型的原始类型列表,则未绑定的通配符类型将不会保持集合的类型不变 ,因为列表最初是类型variant。

 List list1 = new ArrayList(); list1.add(1); list1.add("foo"); List list2 = list1;