Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

What is the difference between Java vararg with direct type vs wildcard generic via extends?

How do following 2 Java method declarations differ:

public <S extends Item> void withExtra1(S... extra) {
    Collections.addAll(pool, extra);
}

and:

public void withExtra2(Item... extra) {
    Collections.addAll(pool, extra);
}
like image 485
gavenkoa Avatar asked Oct 23 '25 04:10

gavenkoa


2 Answers

They don't, since S will be erased to Item, both signatures end up as Item....

like image 173
Kayaman Avatar answered Oct 24 '25 19:10

Kayaman


The Java spec says

A Java compiler must output generic signature information for any class, interface, constructor or member whose generic signature in the Java programming language would include references to type variables or parameterized types.

If you check the bytecode you can see the method with generics has a different signature:

  public <S extends Item> void withExtra1(S...);
    descriptor: ([LItem;)V
    flags: (0x0081) ACC_PUBLIC, ACC_VARARGS
    Code:
      stack=2, locals=2, args_size=2
         0: aload_0
         1: getfield      #2                  // Field pool:Ljava/util/List;
         4: aload_1
         5: invokestatic  #3                  // Method java/util/Collections.addAll:(Ljava/util/Collection;[Ljava/lang/Object;)Z
         8: pop
         9: return
      LineNumberTable:
        line 9: 0
        line 10: 9
      LocalVariableTable:
        Start  Length  Slot  Name   Signature
            0      10     0  this   LItem;
            0      10     1 extra   [LItem;
      LocalVariableTypeTable:
        Start  Length  Slot  Name   Signature
            0      10     1 extra   [TS;
    Signature: #23                          // <S:LItem;>([TS;)V


  public void withExtra2(Item...);
    descriptor: ([LItem;)V
    flags: (0x0081) ACC_PUBLIC, ACC_VARARGS
    Code:
      stack=2, locals=2, args_size=2
         0: aload_0
         1: getfield      #2                  // Field pool:Ljava/util/List;
         4: aload_1
         5: invokestatic  #3                  // Method java/util/Collections.addAll:(Ljava/util/Collection;[Ljava/lang/Object;)Z
         8: pop
         9: return
      LineNumberTable:
        line 13: 0
        line 14: 9
      LocalVariableTable:
        Start  Length  Slot  Name   Signature
            0      10     0  this   LItem;
            0      10     1 extra   [LItem;

#23 in the constant pool is #23 = Utf8 <S:LItem;>([TS;)V

You can access this information at runtime using reflection:

import java.util.Arrays;
import java.util.Collections;
import java.util.List;

public class Item {

    List<Item> pool;

    public static void main(String[] args) {
        for (var m : Item.class.getMethods())
            System.out.println(m.getName() + " " +
                    Arrays.toString(m.getGenericParameterTypes()));
    }


    public <S extends Item> void withExtra1(S... extra) {
        Collections.addAll(pool, extra);
    }

    public void withExtra2(Item... extra) {
        Collections.addAll(pool, extra);
    }

}

stdout:

main [class [Ljava.lang.String;]
withExtra1 [S[]]
withExtra2 [class [LItem;]
wait [long]
wait [long, int]
wait []
equals [class java.lang.Object]
toString []
hashCode []
getClass []
notify []
notifyAll []
like image 25
andresp Avatar answered Oct 24 '25 20:10

andresp



Donate For Us

If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!