请考虑以下来自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<?>
而是警告这种情况。添加注释或强制转换不会单单改善代码,但会降低可读性,甚至可能掩盖有关使用非参数类型的实际有效警告。
答案 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<?>
?
因为这会带来很多限制。