Java generics - 通配符的目的除了下限?
Java generics - purpose of wildcard except for lower bounds?
对无限泛型或上限泛型使用通配符的目的是什么?更具体地说:
为什么我要说 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)
?
如果您永远不会在后面的代码中引用 E
,则无需声明它。
减少程序员的记忆压力。
如果列表的类型在运行时决定,那么您将需要使用通配符。
以下程序将打印 List<String>
if 运行 和任何命令行参数;否则它将打印 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));
}
}
优先使用带通配符的版本。如果参数的类型为 List<?>
,则显然任何 List
都将被接受。如果任何 List
被接受,就没有理由给类型参数一个名字,所以写 <E>
只会很混乱。另一方面,如果类型参数在签名中出现两次,则不能使用通配符。例如,这个签名需要一个类型参数。
public static <E> List<E> combineLists(List<E> list1, List<E> list2)
实际上,在该示例中,如果参数的类型为 List<? extends E>
可能会更好(没有通配符的唯一方法是使用三个类型参数,一团糟)。
在EffectiveJava中,建议即使方法体中需要类型参数,也应该优先选择带有通配符的签名版本,写一个私有的辅助方法来使这成为可能。例如:
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<?>
}
generic method的官方教程已经解释的很清楚了。
... the type parameter T is used only once. The return type doesn't depend on the type parameter, nor does any other argument to the method (in this case, there simply is only one argument). This tells us that the type argument is being used for polymorphism; its only effect is to allow a variety of actual argument types to be used at different invocation sites. If that is the case, one should use wildcards. ...
Generic methods allow type parameters to be used to express dependencies among the types of one or more arguments to a method and/or its return type. If there isn't such a dependency, a generic method should not be used.
对无限泛型或上限泛型使用通配符的目的是什么?更具体地说:
为什么我要说 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)
?
如果您永远不会在后面的代码中引用 E
,则无需声明它。
减少程序员的记忆压力。
如果列表的类型在运行时决定,那么您将需要使用通配符。
以下程序将打印 List<String>
if 运行 和任何命令行参数;否则它将打印 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));
}
}
优先使用带通配符的版本。如果参数的类型为 List<?>
,则显然任何 List
都将被接受。如果任何 List
被接受,就没有理由给类型参数一个名字,所以写 <E>
只会很混乱。另一方面,如果类型参数在签名中出现两次,则不能使用通配符。例如,这个签名需要一个类型参数。
public static <E> List<E> combineLists(List<E> list1, List<E> list2)
实际上,在该示例中,如果参数的类型为 List<? extends E>
可能会更好(没有通配符的唯一方法是使用三个类型参数,一团糟)。
在EffectiveJava中,建议即使方法体中需要类型参数,也应该优先选择带有通配符的签名版本,写一个私有的辅助方法来使这成为可能。例如:
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<?>
}
generic method的官方教程已经解释的很清楚了。
... the type parameter T is used only once. The return type doesn't depend on the type parameter, nor does any other argument to the method (in this case, there simply is only one argument). This tells us that the type argument is being used for polymorphism; its only effect is to allow a variety of actual argument types to be used at different invocation sites. If that is the case, one should use wildcards. ...
Generic methods allow type parameters to be used to express dependencies among the types of one or more arguments to a method and/or its return type. If there isn't such a dependency, a generic method should not be used.