我想最好从我正在看的行为开始:
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
。
但是当我们尝试使用varargs时,这会停止工作。我希望printClassVarargs()
能够识别它的参数类型为String[]
,就像没有varargs的方法识别其参数的类型一样。另请注意,如果我直接调用printClassVarargs()
(它非常高兴输出String
),但只有当它被callPrintClassVarargs()
调用时才会发生这种情况,它会忘记它的类型争论和假设它得到Object
。我也意识到我必须在这里压制一个编译器警告,这通常是在我试图转换泛型时出现的,但是我不确定那里到底发生了什么。
所以我的问题真的是两个。这种行为背后的原因是什么?这是类型擦除的一些后果,还是Java处理数组的方式?其次,有什么方法可以解决这个问题吗?
当然,这只是一个简单的例子。我不是试图以这种方式打印类名,但最初在编写重载方法来连接数组时发现了问题。
答案 0 :(得分:2)
我认为问题归结为无法创建泛型类型的数组。如果修改callPrintClassVarargs
以显式创建新数组实例并将其传递给printClassVarargs
,那么基础问题就会变得明确。
// doesn't work, gives compiler error (cannot create array of generic type)
public static <T> void callPrintClassVarargs(T str) {
printClassVarargs(new T[]{str});
}
//This works
public static <T> void callPrintClassVarargs(T str) {
printClassVarargs(new Object[]{str});
}
What's the reason I can't create generic array types in Java? - 这个问题涉及为什么不可能创建泛型类型的数组,也许同样解释了这个问题。
答案 1 :(得分:2)
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
}
观察主#12,创建字符串obj的新String []以用作printClassVarargs()
和callPrintClassVarargs()
的参数
在主#19 printClassVarargs
上调用,创建的String []作为参数。这导致printClassVarargs
在运行时知道该对象的类型。这种类型得以保留。
在主#23 callPrintClassVarargs
上调用,同时使用创建的String []作为参数。然后在callPrintClassVarargs#1
上创建一个新数组。这次,泛型类型声明中没有可用的类型信息,因此创建了一个新的Object []。 String []存储在此数组中,并传递给callPrintClassVarargs#8
printClassVarargs
,现在必须处理Object [],其componentType是object。
正如您所看到的,当传递给callPrintClassVarargs(T str)
的泛型参数时,参数的类型会被删除。
QED