为什么“收藏”不被简单地视为“收藏<>

2022-09-01 01:48:16

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

Collection fromRealm(String realmName);

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

如果我现在想将此方法与流或可选选项一起使用,如下所示:

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

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

日蚀:

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

javac:

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

由于我无法更改API方法的签名以摆脱此警告,因此我可以注释或简单地转换为如下所示:@SuppressWarnings("unchecked")Collection<?>

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

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


答案 1

原因很简单:

您可以从 与 从 中读取 相同的方式读取 s。但是你不能将 s 添加到 a(编译器禁止这样做),而你可以添加到 a。ObjectCollection<?>CollectionObjectCollection<?>Collection

如果在Java 5发布后编译器已经将every转换为,那么以前编写的代码将不再编译,从而破坏向后兼容性。CollectionCollection<?>


答案 2

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

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

实际上,对于无界通配符集合(),您根本无法向列表中添加任何内容,但(来自Oracle文档):List<?> wildcard = new ArrayList<String>()null

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


推荐