了解Java泛型中的通配符

时间:2011-12-12 21:22:57

标签: java generics

我不确定为什么以下代码中的最后一个语句是非法的。 Integer应该是?的子类型,为什么我不能将其分配给b

List<String> a = new ArrayList<String>();
a.add("foo");

// b is a List of anything
List<?> b = a;

// retrieve the first element
Object c = b.get(0);
// This is legal, because we can guarantee
// that the return type "?" is a subtype of Object

// Add an Integer to b.
b.add(new Integer (1)); 

7 个答案:

答案 0 :(得分:11)

关键是b是指某些类型的列表,但编译器不知道该类型是什么,因此它不会知道是否有效添加Integer。同样也是一件好事,给出了您的示例 - 您将向最初创建的对象添加Integer以保存字符串列表。当然,这些信息在Java执行时会丢失 - 但编译器会尽量保证您的安全。

有关 lot 的更多信息,请参阅Java generics FAQ

答案 1 :(得分:3)

Integer不是?的子类型(必要)。 ?是一个通配符;你应该把它解释为“未知”。

因此List<?>List<Object>不同。您可以将您喜欢的任何内容添加到List<Object>

答案 2 :(得分:2)

引用“b”被声明为List,即“我还不知道的事物列表”。 您可以为此引用分配几乎任何实现,例如List。这就是禁止通过此引用向列表添加任何内容的原因。

答案 3 :(得分:1)

这是因为我们不能保证Integer是参数类型“?”的子类型。

看看这个:

Object c = b.get(0);

这是有效的吗?将始终是Object的子类型。

答案 4 :(得分:1)

对于集合和泛型的粗略经验法则如下:

  • Collection<Foo>是一个集合,你可以从中获得一个Foo,你可以添加一个Foo。
  • Collection<? extends Foo>是一个集合,您可以从中获取Foo,但是您无法添加任何内容

为什么会这样?因为当您说Collection<Foo>时,您对该引用的用户承诺,他们可以在相关对象上调用add(Foo elem)方法。另一方面,当您使用通配符版本时,您将“真实”参数类保留为引用用户的秘密 - 他们知道从集合中提取的任何元素都可以强制转换为Foo,而不是他们可以添加任何Foo。

为什么这有用?因为有许多,很多很多情况下你会编写想要遍历Collection的方法,其元素都是Foos,但你永远不需要添加任何元素。像这样:

public Foo findAFooThatILike(Collection<? extends Foo> foos);

在这里使用通配符意味着该方法将接受Collection<Foo>作为其参数以及Foo的任何子类型的集合;例如,如果Bar是Foo的子类型,则上面的签名意味着您可以将Collection<Bar>传递给该方法。

如果另一方面,你写了这样的签名:

public Foo findAFooThatILike(Collection<Foo> foos);

...然后你能够传递Collection<Bar>作为参数。为什么?因为某些内容属于Collection<Foo>,所以需要支持add(Foo elem)方法,而Collection<Bar>则不需要。{/ p>

请注意,这些经验法则仅适用于Collection接口和类。 (另请注意,Collection<? extends Foo>并不意味着“只读Foo集合”;当您不知道精确的元素类型时,许多从集合中删除元素的方法仍然可以工作。

所以,回到原来的问题:List<?>List<? extends Object>相同。它是一个列表,您可以从中获取对象实例的引用,但您无法安全地添加任何内容。

答案 5 :(得分:1)

以下简要概述了您可以和不能使用泛型的内容:

    List<? extends Number> listOfAnyNumbers = null;
    List<Number> listOfNumbers = null;
    List<Integer> listOfIntegers = null;
    listOfIntegers = listOfNumbers;     // Error - because listOfNumbers may contain non-integers
    listOfNumbers = listOfIntegers;     // Error - because to a listOfNumbers you can add any Number, while to listOfIntegers you cannot.
    listOfIntegers = listOfAnyNumbers;  // Error - because listOfAnyNumbers may contain non-integers  
    listOfAnyNumbers = listOfIntegers;  // OK    - because listOfIntegers is a list of ?, where ? extends Number.
    listOfNumbers = listOfAnyNumbers;   // Error - because listOfAnyNumbers may actually be List<Float>, to which you cannot add any Number. 
    listOfAnyNumbers = listOfNumbers;   // OK    - because listOfNumbers is a list of ?, where ? extends Number.

答案 6 :(得分:0)

列表与LT;?&GT;表示键入未知类型的列表。这可以是整数,字符串或XYZ类的列表。

由于您不知道键入的列表类型,您只能从集合中读取,并且您只能将读取的对象视为对象实例。

如果要在通配符通用集合中插入元素,请转到超级通配符边界。