假设您必须经常调用操作T get(int)
,它从底层数组返回一个对象。基本上,这可以通过两种方式实现:
class GenericArray<T> {
final T[] underlying;
GenericArray(Class<T> clazz, int length) {
underlying = (T[]) Array.newInstance(clazz, length);
}
T get(int i) { return underlying[i]; }
}
和
class ObjectArray<T> {
final Object[] underlying;
ObjectArray(int length) {
underlying = new Object[length];
}
T get(int i) { return (T) underlying[i]; }
}
第一个是使用反射,因此在创建时会更慢。第二个是使用向下转换,这引入了一些开销。由于运行时的泛型类型擦除,必须有一些隐式的转换机制。
这是真的,这两个在get(i)
?
答案 0 :(得分:8)
让我们检查一下字节码:
Compiled from "ObjectArray.java"
class lines.ObjectArray<T> {
final java.lang.Object[] underlying;
lines.ObjectArray(int);
Code:
0: aload_0
1: invokespecial #10 // Method java/lang/Object."<init>":()V
4: aload_0
5: iload_1
6: anewarray #3 // class java/lang/Object
9: putfield #13 // Field underlying:[Ljava/lang/Object;
12: return
T get(int);
Code:
0: aload_0
1: getfield #13 // Field underlying:[Ljava/lang/Object;
4: iload_1
5: aaload
6: areturn
}
Compiled from "GenericArray.java"
class lines.GenericArray<T> {
final T[] underlying;
lines.GenericArray(java.lang.Class<T>, int);
Code:
0: aload_0
1: invokespecial #13 // Method java/lang/Object."<init>":()V
4: aload_0
5: aload_1
6: iload_2
7: invokestatic #16 // Method java/lang/reflect/Array.newInstance:(Ljava/lang/Class;I)Ljava/lang/Object;
10: checkcast #22 // class "[Ljava/lang/Object;"
13: putfield #23 // Field underlying:[Ljava/lang/Object;
16: return
T get(int);
Code:
0: aload_0
1: getfield #23 // Field underlying:[Ljava/lang/Object;
4: iload_1
5: aaload
6: areturn
}
如您所见,get
的字节码相同。
答案 1 :(得分:1)
This answer证明两种get
方法是等价的。这是一个字节码免费答案,解释我如何理解这个主题。请记住,Java中的泛型是使用类型擦除实现的。宽松地说,这意味着T
被Object
取代,并且在必要时插入了强制转换(实际上并不总是Object
- 如果你写class Foo<T extends Number> { ... }
,那么T
在类的主体内被Number
)替换。
这意味着您的ObjectArray
类的代码会转换为类似
class ObjectArray {
final Object[] underlying;
ObjectArray(int length) {
underlying = new Object[length];
}
Object get(int i) {
return underlying[i];
}
}
请注意get
方法中没有强制转换。仅需要(T)
来编译代码。它在运行时没有任何影响,并且永远不会抛出ClassCastException
。
另一个类的代码转换成这样的代码:
class GenericArray {
final Object[] underlying;
GenericArray(Class clazz, int length) {
underlying = (Object[]) Array.newInstance(clazz, length);
}
Object get(int i) {
return underlying[i];
}
}
所以get
方法是等价的。这两个类之间的唯一区别是反射用于生成数组,因此如果您尝试存储错误类型的对象,则会产生ArrayStoreException
。因为只有当你通过使用原始类型滥用泛型时才会发生这种情况,所以在大多数情况下可能不值得使用反射。