I lose the type of a variable in a generic method when moving to a second generic method with varargs

I think it's best to start with the behavior I'm looking for:

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); } } 

Looking at printClass() and callPrintClass() , everything seems to work fine. callPrintClass() takes a common argument and passes it. printClass() recognizes this variable by its correct type, not caring who sent this parameter, and then does as expected and prints java.lang.String .

But when we try to use varargs, it stops working. I would expect printClassVarargs() recognize that the argument is of type String[] , similar to a method without varargs that recognizes the type of its argument. Note also that this does not happen if I call printClassVarargs() directly (he is very pleased with the output of String ), but only when he calls callPrintClassVarargs() , where he forgets the type of his argument and assumes that he receives Object . I also understand that I need to suppress the compiler warning that usually occurs when I try to use generic games, but I'm not sure what exactly is happening there.

So my question is really two. What is the reason for this behavior? Is this some consequence of type erasure or the way Java handles arrays? And secondly, is there a way around this?

This, of course, is a simple example. I am not trying to print class names this way, but initially found a problem when writing an overloaded method to combine arrays.

+4
source share
2 answers

Varargs is syntactic sugar that is translated into an array of this type by the compiler. This means that a method(Type arg...) will become method(Type[] arg) .

In Java, you cannot create arrays of non-recoverable types (types whose type information is lost when erased). Therefore, common varargs, such as printClassVarargs(T ... str) , will be translated to printClassVarargs(Object[] str) , which will actually erase the type information. This is what you observe in your test.

--- EDIT ---

To answer your question (cfr comments) on the difference between printClassVarargs(str) and callPrintClassVarargs(str) , we could take a look at the bytecode of your test class for the necessary hints:

 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 } 

Pay attention to the main number 12, that creates a new line [] of your obj line, which will be used as a parameter for printClassVarargs() and callPrintClassVarargs()

In main # 19, printClassVarargs is called with the created String [] parameter. This causes printClassVarargs know the type of this object at runtime. This type has been saved.

In main # 23, callPrintClassVarargs is called, also with the String [] parameter created. Then, a new array is created on callPrintClassVarargs#1 . This time, type information is not accessible from a type type declaration, so a new object [] is created. String [] is stored in this array and callPrintClassVarargs#8 is passed to printClassVarargs , which should now work with Object [], whose printClassVarargs component is an object.

As you can see, the type of your parameter was erased when passed to the general parameter callPrintClassVarargs(T str) .

QED

+2
source

I think the problem boils down to the impossibility of creating generic type arrays. If callPrintClassVarargs modified to explicitly create a new instance of the array and pass it to printClassVarargs , then the main problem will become apparent.

 // 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 is the reason that I cannot create generic array types in Java? - This question concerns why it is impossible to create arrays of the generic type, perhaps this also explains this problem.

+2
source

All Articles