我想最好从我正在查看的行为开始:
public class genericTest {
public static void main(String[] args) {
String str = "5318008";
printClass(str); // class java.lang.String
callPrintClass(str); // class java.lang.String
printClassVarargs(str); // class java.lang.String
callPrintClassVarargs(str); // class java.lang.Object
}
public static <T> void printClass(T str) {
System.out.println(str.getClass());
}
public static <T> void printClassVarargs(T ... str) {
System.out.println(str.getClass().getComponentType());
}
public static <T> void callPrintClass(T str) {
printClass(str);
}
@SuppressWarnings("unchecked")
public static <T> void callPrintClassVarargs(T str) {
printClassVarargs(str);
}
}
查看 printClass()
和 callPrintClass()
,似乎一切正常。 callPrintClass()
采用通用参数并将其传递。 printClass()
通过其正确类型识别此变量,而不关心是谁发送参数,然后按预期执行并打印 java.lang.String
。
但是当我们尝试使用可变参数时,它就停止工作了。我希望 printClassVarargs()
能够识别它的参数是 String[]
类型,就像没有可变参数的方法识别其参数的类型一样。另请注意,如果我直接调用 printClassVarargs()
就不会发生这种情况(输出 String
非常高兴),但只有当它被 callPrintClassVarargs( )
,它忘记了参数的类型并假定它正在获取一个 Object
。我还意识到我必须在这里禁止编译器警告,这通常在我尝试转换泛型时出现,但我不确定那里到底发生了什么。
所以我的问题真的有两个。这种行为背后的原因是什么?这是类型删除的结果,还是 Java 处理数组的方式?其次,有什么办法解决这个问题吗?
当然这只是一个简单的例子。我不是试图以这种方式打印类名,而是最初在编写重载方法来连接数组时发现了问题。
最佳答案
Varargs 是语法糖,由编译器转换为给定类型的数组。
这意味着 method(Type arg...)
将变为 method(Type[] arg)
。
在 Java 中,您不能创建 Non-reifiable types 的数组(类型信息因删除而丢失的类型)。因此,printClassVarargs(T ... str)
等通用可变参数将转换为 printClassVarargs(Object[] str)
,有效地导致类型信息的删除。这就是您在测试中观察到的。
--- 编辑 ---
为了回答您关于 printClassVarargs(str)
和 callPrintClassVarargs(str)
之间区别的问题(cfr 评论),我们可以查看您的测试类的字节码寻找所需的线索:
public Test();
Code:
0: aload_0
1: invokespecial #8; //Method java/lang/Object."<init>":()V
4: return
public static void main(java.lang.String[]);
Code:
0: ldc #16; //String 5318008
2: astore_1
3: aload_1
4: invokestatic #18; //Method printClass:(Ljava/lang/Object;)V
7: aload_1
8: invokestatic #22; //Method callPrintClass:(Ljava/lang/Object;)V
11: iconst_1
12: anewarray #25; //class java/lang/String
15: dup
16: iconst_0
17: aload_1
18: aastore
19: invokestatic #27; //Method printClassVarargs:([Ljava/lang/Object;)V
22: aload_1
23: invokestatic #31; //Method callPrintClassVarargs:(Ljava/lang/Object;)V
26: return
public static void printClass(java.lang.Object);
Code:
0: getstatic #40; //Field java/lang/System.out:Ljava/io/PrintStream;
3: aload_0
4: invokevirtual #46; //Method java/lang/Object.getClass:()Ljava/lang/Class;
7: invokevirtual #50; //Method java/io/PrintStream.println:(Ljava/lang/Object;)V
10: return
public static void printClassVarargs(java.lang.Object[]);
Code:
0: getstatic #40; //Field java/lang/System.out:Ljava/io/PrintStream;
3: aload_0
4: invokevirtual #46; //Method java/lang/Object.getClass:()Ljava/lang/Class;
7: invokevirtual #59; //Method java/lang/Class.getComponentType:()Ljava/lang/Class;
10: invokevirtual #50; //Method java/io/PrintStream.println:(Ljava/lang/Object;)V
13: return
public static void callPrintClass(java.lang.Object);
Code:
0: aload_0
1: invokestatic #18; //Method printClass:(Ljava/lang/Object;)V
4: return
public static void callPrintClassVarargs(java.lang.Object);
Code:
0: iconst_1
1: anewarray #3; //class java/lang/Object
4: dup
5: iconst_0
6: aload_0
7: aastore
8: invokestatic #27; //Method printClassVarargs:([Ljava/lang/Object;)V
11: return
}
在 main#12 上观察,您的字符串 obj 的新 String[] 已创建,用作 printClassVarargs()
和 callPrintClassVarargs()
的参数
在 main#19 上 printClassVarargs
被调用,创建的 String[] 作为参数。这导致 printClassVarargs
在运行时知道该对象的类型。此类型被保留。
在 main#23 上 callPrintClassVarargs
被调用,同样以创建的 String[] 作为参数。然后在 callPrintClassVarargs#1
上创建一个新数组。这一次,泛型类型声明中没有可用的类型信息,因此创建了一个新的 Object[]。 String[] 存储在此数组中,并在 callPrintClassVarargs#8
上传递给 printClassVarargs
,它现在必须处理一个 Object[],其 componentType 是对象。
如您所见,当传递给 callPrintClassVarargs(T str)
的通用参数时,您的参数类型被删除了。
QED
关于java - 当传递给带有可变参数的第二个泛型方法时,我在泛型方法中丢失了变量的类型,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/11501530/