我正在阅读有关仿制药中未知类型和原始类型的内容,我想到了这个问题。换句话说,是......
Set<?> s = new HashSet<String>();
和
Set s = new HashSet<String>();
......同一个?
我试了一下,他们似乎都做了同样的事情,但我想知道他们是否与编译器有任何不同。
答案 0 :(得分:7)
Set<?> s = HashSet<String>();
s.add(2); // This is invalid
Set s = HashSet<String>();
s.add(2); // This is valid.
关键是,第一个是无界参数化类型Set
。编译器会在那里执行检查,因为除了null
之外你不能向这些类型添加任何东西,编译器会给你一个错误。
虽然第二个是原始类型,但编译器在向其添加任何内容时不会进行任何检查。基本上,你在这里失去了类型安全。
你可以看到那里失去类型安全的结果。在2
的编译时,向集合中添加Set<?>
将失败,但对于原始类型Set
,它将成功添加,但是在运行时可能会抛出异常,当您获得集合中的元素,并将其指定为String
。
除此之外,您应避免在较新的代码中使用原始类型。您很少会找到任何可以使用它的地方。您使用原始类型的几个地方是访问该类型的static
字段,或获取该类型的Class
对象 - 您可以Set.class
,但不能Set<?>.class
。< / p>
答案 1 :(得分:5)
第一个创建Set<?>
,这意味着:“一个未知类的通用集合”。您将无法向此集添加任何内容(null除外),因为编译器不知道其泛型类型是什么。
第二个创建一个原始的非泛型集,您可以添加任何想要的内容。它不提供任何类型安全性。
我不明白你为什么要使用它们。 Set<String>
应该是声明的类型。
答案 2 :(得分:3)
第一个使用泛型,第二个使用Set
的原始形式。
第一个使用通配符作为泛型类型参数。这意味着,“某个特定但未知类型的Set
”,因此您不会调用带有泛型参数的add
等方法,因为编译器不知道它是哪种特定类型真的是。它通过在编译时禁止这样的调用来维护类型安全。
原始表单会删除所有泛型,并且不会提供强类型。您可以向Set
,甚至非String
添加任何内容,这使得以下代码不是类型安全的:
Set<String> genericSet = new HashSet<String>();
Set rawSet = genericSet;
rawSet.add(1); // That's not a String!
// Runtime error here.
for (String s : genericSet)
{
// Do something here
}
当检索到ClassCastException
Integer
并且预计会有1
时,这会产生运行时String
。
保持尽可能多的通用类型信息是可行的方法。
Set<String> s = HashSet<String>();
答案 3 :(得分:3)
Set<?>
告诉编译器该集合包含特定类型,但类型未知。当您尝试使用通用参数调用方法时,编译器会使用此信息来提供错误,例如add(T)
。
Set
告诉编译器该集合是&#34; raw&#34; type,没有给出泛型类型参数。当调用对象的泛型方法时,编译器将引发警告而不是错误。
为了在没有警告的情况下向集合添加元素,您需要指定变量的泛型类型信息。编译器可以推断构造函数的类型参数。像这样:
Set<String> s = new HashSet<>();
此信息允许编译器验证Set
是否以类型安全的方式使用。如果您的代码在没有类型安全警告的情况下编译,并且您不使用任何显式强制转换,则可以确保在运行时不会引发ClassCastException
。如果您使用泛型,但忽略类型安全警告,则可能会在您的源代码中没有投射的位置看到ClassCastException
。