使用通配符进行无界或上限泛型的目的是什么?更具体地说:
为什么我会说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)
?
答案 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参数,也不依赖于该方法的任何其他参数(在这种情况下,仅存在一个参数)。这告诉我们类型参数用于多态。它的唯一作用是允许在不同的调用站点使用各种实际的参数类型。在这种情况下,应使用通配符。 ...
泛型方法允许使用类型参数来表示方法的一个或多个参数的类型和/或其返回类型之间的依赖关系。 如果没有这种依赖性,则不应使用通用方法。