以下两个Java方法声明有何不同:
public <S extends Item> void withExtra1(S... extra) {
Collections.addAll(pool, extra);
}
和:
public void withExtra2(Item... extra) {
Collections.addAll(pool, extra);
}
答案 0 :(得分:4)
它们不会,因为S
将被擦除为Item
,所以两个签名都以Item...
结尾。
答案 1 :(得分:2)
Java规范说
Java编译器必须为任何类,接口,构造函数或成员输出通用签名信息,这些类,接口,构造函数或成员的Java编程语言中的通用签名将包括对类型变量或参数化类型的引用。
如果检查字节码,则可以看到具有泛型的方法具有不同的签名:
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
是#23 = Utf8 <S:LItem;>([TS;)V
您可以在运行时使用反射访问此信息:
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);
}
}
标准输出:
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 []
答案 2 :(得分:1)
这两个签名是等效的,假设调用方未提供明确的类型见证人。一个签名可以接受的任何一组参数都被另一个签名接受:
S
或子类型为S
且等于{{ 1}},所有表达式的类型都等于Item
或子类型,并且被第二个签名接受。Item
或子类型Item
的表达式组成,并且如果推断出S
则可以传递给第一个签名成为Item
(由于Item
在S
的{{1}}范围之内,所以可以)