new ArrayList <set <?>&gt;() - 为什么可以?</set <?>

时间:2012-01-05 07:24:10

标签: java generics

如果

    new ArrayList<?>();  // is not Legal

如果

    new ArrayList<? extends Number>();   // is not Legal,

那为什么

    new ArrayList<Set<?>>();    // IS Legal

有人可以举例解释。

5 个答案:

答案 0 :(得分:2)

创建ArrayList时,必须为其泛型参数指定显式类型。 ?? extends Number不是类型。 Set<?>是一种类型(即Set,我们不关心其泛型类型)。

答案 1 :(得分:1)

已经给出了答案,所以这就是你需要做的事情而不是

首先,如果您不关心列表包含的内容类型:

ArrayList example1 = new ArrayList(); // any object will do.
ArrayList<Object> example2 = new ArrayList<Object>(); // since everything inherits from Object anyway...

那么如果你想添加任意Number个对象怎么办:

ArrayList<Number> example3 = new ArrayList<Number>(); // any Number may be added

最后,之前的答案掩盖了上一个例子的含义。

ArrayList<Set<?>> example4 = new ArrayList<Set<?>>();

那意味着你构造一个ArrayList,你希望在其中找到/放置Set个可能包含任意类型对象的Set。即您不关心Set中的内容,但您确实关心ArrayList您添加到ArrayList

因此,在新声明/创建的Set<?> 所执行的所有操作范围内Set的部分可以自由转换为{{1}} :结果程序将具有等效的语义。

答案 2 :(得分:1)

这种情况会发生,因为在您的具体类型声明中,您使用interface作为泛型参数。想想将来如何使用它:

List someList = new ArrayList<Set<?>>();    
Set<?> someSet = new HashSet<Integer>();
someList.add(someSet);

在第一行 - 您正在定义一个ArrayList类型的列表,它将保留“某些设置”(任何类型)。然后,在第二行中,您将声明并定义具体类型HashSet的集合。它被转换为Set<?>,因此将它添加到以前创建的列表中没有问题。

答案 3 :(得分:0)

那是因为 unknown ?无法转换为任何类型。

new ArrayList<?>();

将导致以下结果(假设可以编译)。

List<?> list = new ArrayList<?>();
list.add(? e); //Java sees this as WTF???

由于此?未知,因此java将其转换为list.add(null e);,这是错误的。

答案 4 :(得分:0)

new ArrayList<?>()不会很有用。考虑通配符的含义是有帮助的:

  • ? extends Foo表示您无法放入任何内容(null除外),但您可以将对象视为Foo
  • ? super Foo表示您可以放入Foo,但无法取出对象(Object除外)
  • ?是十字路口:您不能放入任何内容(null除外),并且您无法获取对象(Object除外)

请注意,List<Foo>可以投放到List<? extends Foo>List<? super Foo>List<?>,但这三者都不能安全地转换为List<Foo>

所以,假设你创建了一个new ArrayList<?>()。你可以指定什么样的参考?只有List<?>。这意味着你将无法放入任何引用(除了null),除了Object之外,你将无法获得任何引用。换句话说,这个泛型比泛型要替换的原始类型稍微有用。

当您看到List<?>List<? extends Foo>时,很有可能有人将其创建为非通配类型,然后将其转换为通配类型。例如:

List<? extends Number> getSomeNumbers() {
    List<Number> numbers = new ArrayList<Number>();
    numbers.add(1);
    numbers.add(2L);
    numbers.add(3.14);
    return numbers; // implicit casting to List<? extends Number>
}

在此示例中,如果numbers已被输入为List<? extends Number>,则add行中没有一行会被编译,因此它不会有用。如果我们将对象构造为new ArrayList<? extends Number>(),我们别无选择,只能以无用的,通配的形式引用它。

同样,如果你创建了一个new ArrayList<? super Number>(),那么添加就可以了,但你要保证没有人可以检索除Object之外的值。您可以通过创建(和引用)new ArrayList<Object>()来完成相同的工作。

那么,为什么允许new ArrayList<Set<?>>()?因为 有用;我们可以将元素添加到列表中,也可以检索它们。当我们创建每个集合时,我们将创建它而不使用通配符,以便我们可以添加它,然后我们将其删除并将其添加到列表中。

List<Set<?>> getSomeSets() {
    List<Set<?>> list = new ArrayList<Set<?>>();
    Set<Number> set = new HashSet<Number>();
    set.add(1);
    set.add(2L);
    set.add(3.14);
    Set<?> wildcardSet = set; // allowed, though not actually needed
    list.add(wildcardSet);
    list.add(set); // don't need to go through intermediate wildcardSet
    return list;
}