Java,Generics:Set <! - ? - > s = HashSet <string>()和Set s = HashSet <string>()?</string> </string>之间有什么区别?

时间:2014-02-26 17:39:00

标签: java generics

我正在阅读有关仿制药中未知类型和原始类型的内容,我想到了这个问题。换句话说,是......

Set<?> s = new HashSet<String>();

Set s = new HashSet<String>();

......同一个?

我试了一下,他们似乎都做了同样的事情,但我想知道他们是否与编译器有任何不同。

4 个答案:

答案 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