为什么不将Collection简单地视为Collection <!-?->

时间:2019-04-04 11:00:48

标签: java generics

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

Collection fromRealm(String realmName);

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

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

principals.fromRealm(realmName).stream().collect(Collectors.toSet());

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

Eclipse:

  

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

javac:

  

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

由于我无法更改API方法的签名来摆脱此警告,因此可以用@SuppressWarnings("unchecked")进行注释,也可以像这样简单地转换为Collection<?>

((Collection<?>) principals.fromRealm(realmName)).stream().collect(Collectors.toSet());

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

4 个答案:

答案 0 :(得分:70)

原因很简单:

您可以像从Object一样从Collection<?>阅读Collection但是,您不能将Object添加到Collection<?>(编译器禁止这样做),而可以添加到Collection

如果Java 5发行后编译器将每个Collection都转换为Collection<?>,那么以前编写的代码将不再编译,因此将破坏向后兼容性。

答案 1 :(得分:19)

原始类型和无界通配符<?>之间的主要区别在于后者是类型安全的,也就是说,在编译级别,它检查集合中的项是否属于相同的类型。编译器不允许您将字符串和整数添加到通配符类型的集合中,但 允许您执行以下操作:

List raw = new ArrayList();
raw.add("");
raw.add(1);

实际上,对于 unbounded 通配符集合(List<?> wildcard = new ArrayList<String>()),您只能添加null(来自Oracle docs的列表,而不能添加任何内容):

  

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

答案 2 :(得分:3)

我可以想到一个为什么不将Collection视为Collection<?>的用例,假设我们有一个ArrayList的实例

现在,如果实例的类型为ArrayList<Integer>ArrayList<Double>ArrayList<String>,则只能添加该类型(类型检查)。 ArrayList<?>不等同于ArrayList<Object>

但是只有ArrayList,您可以添加任何类型的对象。这可能是编译器不将ArrayList视为ArrayList<?>(类型检查)的原因之一。

另一个原因可能是与没有泛型的Java版本向后兼容。

答案 3 :(得分:1)

Collection<?>尖叫:

  

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

Collection说:

  

太酷了!您可以添加任何您喜欢的内容,我没有限制。

那么,为什么编译器不应该将Collection转换为Collection<?>? 因为这会带来很多限制。