Java泛型 - 除了下限以外通配符的用途是什么?

5
使用通配符来表示无界或上界泛型的目的是什么?更具体地说:
为什么要这样写:public static void foo(List<?> bar) 而不是 public static <E> void foo(List<E> bar)
为什么要这样写:public static void foo(List<? extends Baz> bar) 而不是 public static <E extends Baz> void foo(List<E> bar)

简洁明了,也许? - aroth
4个回答

4
如果您在接下来的代码中不会引用E,则无需声明它。
减轻程序员的记忆负担。

1
没错,这正是我想说的。泛型类型存在的目的是为了让你能够对其进行操作,而不仅仅是作为语法糖存在。 - asteri

3

使用通配符的版本更受欢迎。如果参数的类型为List<?>,则清楚任何List都将被接受。如果接受任何List,则没有理由给类型参数命名,因此写<E>只会增加混乱。另一方面,如果类型参数在签名中出现两次,则不能使用通配符。例如,此签名需要一个类型参数。

public static <E> List<E> combineLists(List<E> list1, List<E> list2)

实际上,在这个例子中,如果参数的类型为List<? extends E>,可能会更好(如果没有通配符,唯一的方法是有三个类型参数,非常混乱)。

在《Effective Java》中,建议即使在方法体中需要类型参数,也应该优先使用带通配符的签名版本,并编写一个私有的辅助方法来实现这一点。例如:

public static void swapFirstAndLast(List<?> list) {
    helper(list);
}

private static <E> void helper(List<E> list) {
    int size = list.size();
    E e = list.get(0);
    list.set(0, list.get(size - 1)); // These lines would not be possible
    list.set(size - 1, e);           // If the type of list were List<?>
}

除了实际类型参数之外,您可以放置通配符。这意味着,虽然这是有效的语法:public static <E> List<?> combineLists(List<?> list1, List<?> list2),但是如果您尝试实现它,您会讨厌自己。 - Makoto
@Makoto 你可以写出来,但是这样会不太安全,因为你无法确定任何一个列表是否具有共同的类型,而 <? extends E> 可以确保上界 E,这样就会导致这种情况非法:combineList(asList(""), asList(0)) - undefined

2

如果列表的类型在运行时决定,那么您需要使用通配符。

以下程序将打印一个List<String>,如果使用任何命令行参数运行;否则它将打印一个List<Number>

public class Test {

    public static List<String> listOfStrings = Arrays.asList("hello", "world");
    public static List<Number> listOfNumbers = Arrays.asList(1, 2, 3, 4.5);

    public static List<?> getListOfUnknown(boolean arg) {
        if(arg) return listOfStrings;
        else return listOfNumbers;
    }

    public static void main(String[] args) {
        System.out.println(getListOfUnknown(args.length > 0));
    }
}

使用OP的推理方式,您可以这样做:public <T extends Object> List<T> getListOfUnknown(boolean arg)。并不是我在推荐这种方法。 - asteri
@asteri 但是你需要调用 <String>getListOfUnknown<Number>getListOfUnknown,因为 T 需要在编译时知道。 - user253751
啊,我明白你在说什么了。抱歉,我只读了方法的签名,没有读到方法体。我的错误。 - asteri

0

通用方法的官方教程已经讲得很清楚了。

...类型参数T只使用了一次。返回类型不依赖于类型参数,也没有任何其他方法参数(在这种情况下,只有一个参数)。这告诉我们,类型参数被用于多态性;它的唯一效果是允许在不同的调用站点使用各种实际参数类型。如果是这种情况,应该使用通配符。...

通用方法允许使用类型参数来表示方法的一个或多个参数以及/或其返回类型之间的依赖关系。如果没有这样的依赖关系,则不应使用通用方法。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接