Java泛型 - 除了下界之外的通配符的用途?

时间:2015-01-08 01:17:17

标签: java generics

使用通配符进行无界或上限泛型的目的是什么?更具体地说:

为什么我会说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)

4 个答案:

答案 0 :(得分:4)

如果您不打算在后面的代码中引用E,则无需声明它。

减少程序员的记忆力。

答案 1 :(得分: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));
    }
}

答案 2 :(得分:2)

首选带通配符的版本。如果参数的类型为List<?>,则很明显会接受任何List。如果接受任何List,则没有理由为type参数指定名称,因此编写<E>只会变得混乱。另一方面,如果类型参数在签名中出现两次,则无法使用通配符。例如,此签名需要一个类型参数。

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

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

在Effective Java中,建议即使方法体中需要type参数,也应该更喜欢带有通配符的签名版本,并编写一个私有帮助方法来实现这一点。例如:

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<?>
}

答案 3 :(得分:0)

generic method的官方教程已经讲得足够好了。

  

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

     

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