以下打印范围内所有元素的方法之间是否存在实际差异?
public static void printA(Iterable<?> range)
{
for (Object o : range)
{
System.out.println(o);
}
}
public static <T> void printB(Iterable<T> range)
{
for (T x : range)
{
System.out.println(x);
}
}
显然,printB
涉及对Object的额外检查转换(参见第16行),这对我来说似乎相当愚蠢 - 不是所有的对象都不是吗?
public static void printA(java.lang.Iterable);
Code:
0: aload_0
1: invokeinterface #18, 1; //InterfaceMethod java/lang/Iterable.iterator:()Ljava/util/Iterator;
6: astore_2
7: goto 24
10: aload_2
11: invokeinterface #24, 1; //InterfaceMethod java/util/Iterator.next:()Ljava/lang/Object;
16: astore_1
17: getstatic #30; //Field java/lang/System.out:Ljava/io/PrintStream;
20: aload_1
21: invokevirtual #36; //Method java/io/PrintStream.println:(Ljava/lang/Object;)V
24: aload_2
25: invokeinterface #42, 1; //InterfaceMethod java/util/Iterator.hasNext:()Z
30: ifne 10
33: return
public static void printB(java.lang.Iterable);
Code:
0: aload_0
1: invokeinterface #18, 1; //InterfaceMethod java/lang/Iterable.iterator:()Ljava/util/Iterator;
6: astore_2
7: goto 27
10: aload_2
11: invokeinterface #24, 1; //InterfaceMethod java/util/Iterator.next:()Ljava/lang/Object;
16: checkcast #3; //class java/lang/Object
19: astore_1
20: getstatic #30; //Field java/lang/System.out:Ljava/io/PrintStream;
23: aload_1
24: invokevirtual #36; //Method java/io/PrintStream.println:(Ljava/lang/Object;)V
27: aload_2
28: invokeinterface #42, 1; //InterfaceMethod java/util/Iterator.hasNext:()Z
33: ifne 10
36: return
答案 0 :(得分:7)
在您的示例中,泛型类型在签名的正好一个中使用。在这种情况下,类型T
与调用者的通配符相比没有优势。在您的示例中,该类型对于方法的实现者也没有的优势。
我发现调用者更容易理解通配符版本,因为它明确地说“我根本不关心类型”。
在您的示例中,checkcast
确实是多余的。如果T
有限,则需要它,例如T extends Number
。然后需要Number
的校验广播,因为局部变量x
的类型为Number
,但Iterator.next()
方法仍会返回Object
。似乎Java编译器不打算优化演员表。 JIT可能会在运行时这样做。
<强>更新强>
如果在几个地方使用泛型类型,比如在cletus的答案中,你别无选择,只能使用泛型类型T
,否则编译器看不到参数类型/返回类型之间没有连接(任何两个通配符对于编译器来说是不同的。)
边界情况是签名仅在一个位置具有类型,但实现需要它是通用类型而不是通配符。想一下void swap(List<T> list, int a, int b)
方法。它需要从列表中取出元素并将它们重新放入.IIRC, Effective Java 建议使用带有通配符的公共方法,以及带有包含实际实现的类型的内部帮助器方法。这样,用户就可以获得一个简单的API,并且实现者仍然具有类型安全性。
public void swap(List<?> list, int a, int b){
swapHelper(list, a, b);
}
private <T> void swapHelper(List<T> list, int a, int b){
...
}
答案 1 :(得分:2)
第二种更灵活。一个更好的例子是:它说的是类型。这对你有用是否取决于函数的作用。
当你想从方法中返回一些东西时,第二个显示它的用处:
public static <T> List<T> reverse(List<T> list) {
for (int i=0; i<n/2; i++) {
T value = list.get(i);
list.set(i, list.get(list.size() - i - 1));
list.set(list.size() - i = 1, value);
}
return list;
}
这可能是复制数组的一个更好的例子,但关键是你无法用List<?>
完成上述操作。
答案 2 :(得分:0)
不是真的。生成的字节码应该几乎相同。