泛型的泛型和更通用的<! - ? - >赋值

时间:2012-05-25 09:56:49

标签: java generics

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

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<?>>


我总能找到解决这类问题的办法,但在这种情况下,我真的想知道为什么不允许这样做,解决问题。 < / p>

5 个答案:

答案 0 :(得分:5)

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

java generics covariance

答案 1 :(得分:5)

鉴于此:

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>的协变?是的。因此它编译。<​​/ p>

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

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>>();

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

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

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

  1. 最内层:Object? Extends Object兼容吗?当然可以。

  2. 最外层:HashSet<Object>? extends Set<? extends Object>兼容吗?确定是。

  3. 在数字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>>();
    

答案 2 :(得分: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<?>>();

答案 3 :(得分:0)

Set<Class<?>> 

这说:一组任何类型的类

HashSet<Class<Object>>

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

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

答案 4 :(得分:-1)

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

因此你必须写下面的东西

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

您可以在Object

中存储任何类型的Set