Java 泛型 : wildcard<? > vs 类型参数<E>?

标签 java generics

我正在刷新关于 Java 泛型的知识。所以我转向了 Oracle 的优秀教程……并开始为我的同事准备一个演示文稿。我在 tutorial 中遇到了通配符部分。上面写着:

Consider the following method, printList:

public static void printList(List<Object> list) {
...

The goal of printList is to print a list of any type, but it fails to achieve that goal — it prints only a list of Object instances; it cannot print List<Integer>, List<String>, List<Double>, and so on, because they are not subtypes of List<Object>. To write a generic printList method, use List<?>:

public static void printList(List<?> list) {

我了解 List<Object>不管用;但我将代码更改为

static <E> void printObjects(List<E> list) {
    for (E e : list) {
        System.out.println(e.toString());
    }
}
...
    List<Object> objects = Arrays.<Object>asList("1", "two");
    printObjects(objects);
    List<Integer> integers = Arrays.asList(3, 4);
    printObjects(integers);

你猜怎么着;使用 List<E>我可以毫无问题地打印不同类型的列表。

长话短说:至少教程表明一个需要通配符来解决这个问题;但如图所示,也可以通过这种方式解决。那么,我错过了什么?!

(旁注:使用 Java7 测试;所以这可能是 Java5、Java6 的问题;但另一方面,Oracle 似乎在更新他们的教程方面做得很好)

最佳答案

您使用泛型方法的方法比使用通配符的版本更强大,所以是的,您的方法也是可能的。但是,本教程没有声明使用通配符是唯一可能的解决方案,因此本教程也是正确的。

与泛型方法相比,您使用通配符获得的好处:您必须编写更少的代码,并且界面“更干净”,因为非泛型方法更容易掌握。

为什么泛型方法比通配符方法更强大:你给参数一个你可以引用的名字。例如,考虑一个删除列表的第一个元素并将其添加到列表后面的方法。使用泛型参数,我们可以做到以下几点:

static <T> boolean rotateOneElement(List<T> l){
    return l.add(l.remove(0));
}

使用通配符,这是不可能的,因为 l.remove(0) 会返回 capture-1-of-?,但是 l.add 需要 capture-2-of-?。即,编译器无法推断出 remove 的结果与 add 期望的类型相同。这与第一个例子相反,编译器可以推断出两者都是相同类型的T。此代码无法编译:

static boolean rotateOneElement(List<?> l){
    return l.add(l.remove(0)); //ERROR!
}

那么,如果你想要一个带有通配符的 rotateOneElement 方法,你可以怎么做,因为它比通用解决方案更容易使用?答案很简单:让通配符方法调用泛型方法,然后就可以了:

// Private implementation
private static <T> boolean rotateOneElementImpl(List<T> l){
    return l.add(l.remove(0));
}

//Public interface
static void rotateOneElement(List<?> l){
     rotateOneElementImpl(l);
}

标准库在很多地方都使用了这个技巧。其中之一是 IIRC、Collections.java

关于Java 泛型 : wildcard<? > vs 类型参数<E>?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/22860582/

相关文章:

java - DBUnit 和 Oracle JDBC (ClassCastException)

java - 将正确的 Java 版本传达给 Hadoop

java - 当 child 被 orphanRemoval 删除时,EntityListener 不起作用

python - 无法对通用对象进行猴子修补

swift - Swift实现通用功能

当通过参数传递泛型类型时,Swift 无法推断泛型类型

ios - 泛型属性

Java:如何从 HTML 标签中剥离文本内容?

java - 无法将裸机 web 应用程序部署到运行 openjdk 11 的 tomee 7.1.0

c# - 有没有办法让 C# 泛型处理这种情况