通配符与通用方法

fre*_*low 13 java generics wildcard

打印范围内的所有元素的以下方法之间是否存在实际差异?

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);
    }
}
Run Code Online (Sandbox Code Playgroud)

显然,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
Run Code Online (Sandbox Code Playgroud)

Chr*_*rau 7

在您的示例中,泛型类型仅用于签名的一个位置.在这种情况下,类型对调用者T没有优于通配符的优势.在您的示例中,该类型对于方法的实现者也没有任何优势.

我发现调用者更容易理解通配符版本,因为它明确地说"我根本不关心类型".

在你的例子中,这checkcast确实是多余的.如果T是有限的,那将是必需的T extends Number.然后需要checkcast Number,因为局部变量x将是类型Number,但Iterator.next()方法仍然返回Object.似乎Java编译器不打算优化演员表.JIT可能会在运行时这样做.

更新:

如果泛型类型在很多斑点,就像在克莱的答案时,你别无选择,只能使用一个泛型类型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){
    ...
}
Run Code Online (Sandbox Code Playgroud)