2012-07-16 70 views
3

我猜它可能是最好的开始与行为,我看:我传递到第二泛型方法可变参数,当失去的通用方法变量的类型

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处理数组的方式?其次,有没有办法解决这个问题?

这当然只是一个简单的例子。我没有试图用这种方式打印类名,但最初在编写重载方法来连接数组时发现了问题。

+0

我发现类似的问题:http://stackoverflow.com/questions/1817524/generic-arrays-in-java。 – 2012-07-16 09:41:27

回答

2

可变参数是语法糖,编译器将其转换为给定类型的数组。 这意味着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的创建要用作参数printClassVarargs()callPrintClassVarargs()

在主#19调用,并创建String []作为参数。这导致printClassVarargs在运行时知道该对象的类型。这种类型被保留。

在主#23 callPrintClassVarargs被调用,也与创建的String []作为参数。然后在callPrintClassVarargs#1上创建一个新数组。这次,泛型类型声明中没有可用的类型信息,因此会创建一个新的Object []。字符串[]存储在这个数组中,并在callPrintClassVarargs#8传递给printClassVarargs,它现在必须处理一个Object [],其componentType是object。

正如您可以观察到的,当传递给通用参数callPrintClassVarargs(T str)时,参数的类型被擦除。

QED

+1

我对此不太确定。如果您拨打printClassVarargs("two", "strings"),它将打印String,而不是Object。它肯定有一些想法,我传递给它的是什么样的数组。 – jalopezp 2012-07-16 10:36:33

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? - 这跟为什么它是不可能创建泛型类型的数组问题的交易,也许同样的解释这个问题为好。

+0

这很有道理。我想我不知道这是可变参数如何工作;我一直认为是他们创造阵列。但是如果数组是由调用者创建的,那么只要调用者知道类型就没有问题,我们可以找到正确的。 – jalopezp 2012-07-16 10:39:20

相关问题