为什么Collection不被简单地视为Collection <?>

dpr*_*dpr 40 java generics

请考虑以下来自Shiro org.apache.shiro.subject.PrincipalCollection界面的API方法,但可能也存在于其他库中:

Collection fromRealm(String realmName);
Run Code Online (Sandbox Code Playgroud)

是的,即使是现在,仍然有一些库使用原始类型,可能是为了保留Java 1.5之前的兼容性?

如果我现在想将此方法与流或类似的可选项一起使用:

principals.fromRealm(realmName).stream().collect(Collectors.toSet());
Run Code Online (Sandbox Code Playgroud)

我收到有关未经检查的转换和使用原始类型的警告,我应该更喜欢使用参数化类型。

日食:

类型安全:方法collect(Collector)属于原始类型Stream。泛型类型Stream <T>的引用应参数化

javac:

注意:GenericsTest.java使用未经检查或不安全的操作。

由于我无法更改API方法的签名来摆脱此警告,因此我可以使用以下注释@SuppressWarnings("unchecked")或将其强制转换为Collection<?>

((Collection<?>) principals.fromRealm(realmName)).stream().collect(Collectors.toSet());
Run Code Online (Sandbox Code Playgroud)

由于这种转换当然总是有效,所以我想知道为什么编译器不只是简单地将其Collection视为Collection<?>而是警告这种情况。添加注释或强制转换不会单单提高代码,但会降低可读性,甚至可能掩盖有关使用非参数类型的实际有效警告。

Lin*_*ica 70

原因很简单:

您可能以Object与from Collection<?>相同的方式阅读Collection但是您不能将Objects 添加到Collection<?>(编译器禁止这样做),而Collection可以添加。

如果在Java 5发行后,编译器已将每次转换CollectionCollection<?>,则先前编写的代码将不再编译,因此将破坏向后兼容性。

  • 只是为了避免印象“ Collection &lt;?&gt;”是不可变的:您可以将“ null”添加到“ Collection &lt;?&gt;”,此外,还可以将“ Collection &lt;?&gt;”传递给期望Collection &lt;T&gt;,然后可以添加类型为T的元素,即集合中已经存在的元素的重复项。作为一个实际示例,您可以使用`List &lt;?&gt;`调用`Collections.swap(list,ix1,ix2)`。而且,当然,删除元素总是可行的。但是您不能像原始类型的“集合”那样向其添加任意的“对象”。 (5认同)
  • 要补充一点:`Collection &lt;?&gt;`是_not_“任何类型的集合”。这是“某种类型的集合,但是该类型是未知的”。这就是为什么您不能向其添加非null对象的原因:编译器试图告诉您要添加这些对象,您必须知道Collection的`&lt;T&gt;`是那些对象的超类(而您实际上不了解有关T的任何信息,因为您将Collection键入为&lt;&lt; &gt;&gt;)。 (3认同)
  • FWIW:我认为Scala的存在性类型语法也可能有助于理解Collection &lt;?&gt;的含义。Scala的等效项是`Collection [_]`。这是`Collection [T] forSome {type T}`的简写。后者应理解为_这是`T`的实例的集合。有一些特定的类型等于T,但在code_的此位置未知。 (3认同)

Ond*_* K. 19

原始类型和无界通配符之间的主要区别在于<?>后者是安全类型,即在编译级别,它检查集合中的项目是否属于同一类型。编译器不会让你的字符串和整数添加到通配符类型的集合,但它允许你这样做:

List raw = new ArrayList();
raw.add("");
raw.add(1);
Run Code Online (Sandbox Code Playgroud)

实际上,在无限制的通配符集合(List<?> wildcard = new ArrayList<String>())的情况下,您什么都不能添加到列表中,而只能nullOracle文档中添加

由于我们不知道c的元素类型代表什么,因此无法向其添加对象。add()方法采用类型E(集合的元素类型)的参数。当实际类型参数为?时,代表某种未知类型。我们传递来添加的任何参数都必须是该未知类型的子类型。由于我们不知道是什么类型,因此无法传递任何内容。唯一的例外是null,它是每种类型的成员。

  • “ [[无界通配符&lt;?&gt;]是类型安全的,也就是说,在编译级别,它将检查集合中的项目是否属于同一类型。” -啊?这与您的其余答案相矛盾。-也许不是;可以说它*是*类型安全的,因为它不允许*任何*对象放入`&lt;?&gt;`的集合中。 (2认同)

bvd*_*vdb 6

一个Collection<?>尖叫:

请不要向我添加任何东西。我有一个严格的内容类型,......嗯,我只是忘记了它是什么类型。

而 aCollection说:

这一切都很酷!你可以添加任何你喜欢的,我没有限制。

那么,为什么编译器不应该转换CollectionCollection<?>?因为它会设置很多限制。

  • 我真的很喜欢这个解释,非常基本,但切中要害且易于理解 +1 :) (2认同)