使用通配符的Java集合

时间:2020-03-05 18:55:12  来源:igfitidea点击:
public static void main(String[] args) {

    List<? extends Object> mylist = new ArrayList<Object>();

    mylist.add("Java"); // compile error

}

上面的代码不允许我们将元素添加到列表中,通配符只能用作方法中的签名,同样不能用于添加,而只能用于访问。
在这种情况下,上面的目的是什么?

解决方案

回答

对于使用通配符的Java泛型,假设只打算从通配符中读取,则可以使用上述声明。

不允许添加/写入它,因为必须在编译时剥离所有泛型类型,并且在编译时,编译器无法知道List仅是字符串(它可以是任何对象,包括字符串! )

但是,我们可以从中读取内容,因为它们至少将是对象。在Java集合中不允许混合使用不同的类型,以保持事物的清洁和可理解性,这有助于确保这一点。

回答

有界通配符类型的要点是它们在方法签名中的使用,以增加API的灵活性。例如,如果实现一个通用的Stack <E>,则可以提供一种将多个元素压入堆栈的方法,如下所示:

public void pushAll(Iterable<? extends E> elements) {
    for(E element : elements){
       push(e);
    }
}

与不带通配符的pushAll(Iterable &lt;E> elements)签名相比,它的优点是它允许将E的子类型的集合正常传递给方法,因为`Iterable <String有点违反直觉,它不是Iterable <Object>的子类。

回答

这有效:

List<? super Object> mylist = new ArrayList<Object>();
mylist.add("Java"); // no compile error

从O'Reilly的Java泛型:

The Get and Put Principle: use an extends wildcard when you only get values our of a structure, use a super wildcard when you only put values into a structure, and don't use a wildcard you both get and put.

回答

约书亚·布洛赫(Joshua Bloch)在他的著作《有效的Java》(第二版)中解释了他所谓的使用泛型的生产者/消费者原则。 Josh的解释应该告诉我们为什么示例不起作用(编译)...

第5章(泛型)可在此处免费获得:http://java.sun.com/docs/books/effective/generics.pdf

有关这本书(和作者)的更多信息,请访问:http://java.sun.com/docs/books/effective/

回答

`清单<?扩展了Object>,与List <?>相同,实现了将所有类型List <String>,List <Number>,List <Object>等归纳的目的。类型,并以正确的类型代替"?")。所有这些类型的值都可以分配给类型为List <?>的变量(这与List <Object>!有所不同!)。

通常,我们不能将字符串添加到此类列表。但是,我们可以从列表中读取"对象",也可以向其中添加"空"。我们还可以计算列表的长度,等等。这些是可以保证对每种类型都适用的操作。

有关通配符的良好介绍,请参见论文"将通配符添加到Java编程语言"。这是一篇学术论文,但仍然很容易阅读。

回答

假设我们有一个接口和两个类:

interface IResult {}
class AResult implements IResult {}
class BResult implements IResult {}

然后,我们将得到返回结果列表的类:

interface ITest<T extends IResult> {
  List<T> getResult();
}

class ATest implements ITest<AResult> {
  // look, overridden!
  List<AResult> getResult();
}

class BTest implements ITest<BResult> {
  // overridden again!
  List<BResult> getResult();
}

当我们需要"协变量返回",但是我们返回集合而不是自己的对象时,这是一个很好的解决方案。最大的好处是,在独立于ITest接口使用ATest和BTest时,我们不必强制转换对象。但是,当使用ITest接口时,由于无法确定列表真正包含的对象类型,因此无法将任何内容添加到返回的列表中!如果允许,则可以将BResult添加到List <AResult>(作为List <?扩展T>返回),这没有任何意义。

因此,我们必须记住以下内容:List <? X>扩展定义了一个可以轻松覆盖的列表,但它是只读的。