7

有时我只是没有得到泛型。我经常在代码中使用最通用的集合版本。例如,如果我需要一组任何东西,我会写如下内容:

Set<?> set1 = new HashSet<Object>();

它是编译器允许的,为什么不应该 -Set<?>Set<Object>(甚至更通用..)一样通用。但是,如果我使用“泛型的泛型”使其“更通用”就行不通:

Set<Class<?>> singletonSet = new HashSet<Class<Object>>(); // type mismatch

到底是怎么回事?为什么Set<Object>可分配给Set<?>Set<Class<Object>>不可分配给Set<Class<?>>


我总能找到解决这类问题的方法,但在这种情况下,我真的很想知道为什么不允许这样做,而不是解决方法。

4

5 回答 5

6

鉴于这种:

Set<?> set1 = new HashSet<Great>();

如果您可视化无界通配符的含义,则可以将无界通配符上的开槽类型与 进行比较extends,因此如果您明确地这样做。

Set<? extends Object> set1 = new HashSet<Great>();

阅读,是伟大的扩展对象吗?是的,这样编译。

然后给出这个:

Set<Class<Great>> set3 = new HashSet<Class<Great>>();

至于它为什么起作用,如果你提取 Set 和 HashSet 的参数,它们是Class<Great>,这两个Class<Great>是完全相同的类型。

如果没有通配符,则直接逐字比较类型。

如果我们编写 set3 使其接受协变类型(编译):

Set<? extends Class<Great>> set3a = new HashSet<Class<Great>>();

要阅读,HashSet 的Class<Great>类型与 Set 的类型兼容还是协变Class<Great>?是的。因此它编译。

尽管如果它们只是完全相同的类型,当然没有人会编写那种变量声明,但这是多余的。编译器使用通配符来确定赋值右侧的泛型的具体类或接口参数是否与左侧泛型的具体/接口兼容(理想情况下是接口,如下所示)。

List<? extends Set<Great>> b = new ArrayList<HashSet<Great>>();

阅读它,是HashSet<Great>协变的Set<Great>吗?是的。因此它编译


所以让我们回到你的代码场景:

Set<Class<?>> set3 = new HashSet<Class<Object>>();

在这种情况下,同样的规则适用,您从最里面开始阅读它,对象是否与通配符兼容?是的。然后,您转到下一个最外面的类型,它恰好没有通配符。因此,在没有通配符的情况下,编译器将在Class<Object>和之间进行逐字检查Class<?>,它们不相等,因此会出现编译错误。

如果它在最外面有通配符,那将编译。所以你可能的意思是这个,它编译

Set<? extends Class<?>> singletonSet = new HashSet<Class<Object>>();

让我们做一个更有启发性的例子,让我们使用接口(类是具体类型),比如 Set。这编译:

List<? extends Set<?>> b = new ArrayList<HashSet<Object>>();

因此,从内到外阅读它以找出该代码编译的原因,并明确执行:

  1. 最里面:Object兼容? Extends Object吗?是的。

  2. 最外层:是否HashSet<Object>兼容? extends Set<? extends Object>?是的。

在数字 1 上,它是这个(编译):

Set<? extends Object> hmm = new HashSet<Object>();

在数字 2 上,它是这个(编译):

List<? extends Set<? extends Object>> b = new ArrayList<HashSet<Object>>();

现在让我们尝试删除最外面的通配符,编译器不会进行类型兼容/协变检查,现在将逐字比较。

所以你现在知道下面的答案了,这些可以编译吗?

List<Set<?>> b = new ArrayList<HashSet<Object>>();

// this is same as above:
List<Set<? extends Object>> b = new ArrayList<HashSet<Object>>();

所以你已经猜到了,正确的......这不会编译:-)

要更正上述问题,请执行以下任一操作:

List<? extends Set<? extends Object>> b = new ArrayList<HashSet<Object>>();

List<? extends Set<?>> b = new ArrayList<HashSet<Object>>();

然后,要更正您的代码,请执行以下任一操作:

Set<? extends Class<? extends Object>> singletonSet = 
                                       new HashSet<Class<Object>>();

Set<? extends Class<?>> singletonSet = new HashSet<Class<Object>>();
于 2012-05-30T20:09:59.343 回答
5

泛型在 Java 中不是协变的。这个问题可以帮助你:

java泛型协方差

于 2012-05-25T10:06:48.883 回答
0

我在jdk1.6.0_22下的eclipse中尝试了以下:

Set<Class<?>> singletonSet = new HashSet<Class<Object>>();

编译器给了我以下信息:

Type mismatch: cannot convert from HashSet<Class<Object>> to Set<Class<?>>

我把它改成了这个,它起作用了:

HashSet<Class<Object>> singletonSet = new HashSet<Class<Object>>();

似乎泛型的泛型必须具有相同的集合类和收集的对象的相同类。

这似乎是 java 中的一条规则,可能是因为运行时容易出错,这可能导致严重崩溃。所以以这种方式编写它也可以工作:

HashSet<Class<?>> singletonSet = new HashSet<Class<?>>();
于 2012-05-25T10:08:00.100 回答
0
Set<Class<?>> 

这说:一组任何类型的类

HashSet<Class<Object>>

这表示:必须是 Object 类型的类的 HashSet - 因此只允许 Object.class 对象 - 这没有多大意义,因为 Object 只有一个类对象。

我认为这个问题仍然有效,但也许您可以选择与使用 Class 不同的示例。

于 2012-05-25T10:23:04.227 回答
-1

这里的答案是它接受任何类型的 Object 类型。

因此,您必须在下面写

Set<Class<?>> val = new HashSet<Class<?>>();

在这里你可以存储任何类型ObjectSet

于 2012-05-25T10:08:38.187 回答