我知道在 Java 中泛型类型信息在编译时被删除,因此不能创建泛型类型的数组(因为那样在运行时就不可能有效地强制插入到数组中的类型)。
但为什么不破例呢?为什么不只保留数组的通用类型信息(并且只保留它们)?
这背后的设计决策是什么?在我看来,这会让生活更轻松,有可能做到这一点。
T[] genericArray = new T[10];
最佳答案
简答:
这是因为泛型是帮助编译器帮助你捕捉类型的元数据
错误,所有内容都被编译为使用最小公分母
(通常是 Object
)并进行类型转换。这不是用数组完成的,因为数组
是他们自己的类(class)。 IE。一个ArrayList<String>
和一个 ArrayList<Number>
都有类 ArrayList
,而是一组 String
有课String[]
和一系列
Number
有课Number[]
.
长答案:
编译时,所有使用泛型的都将使用最少的
共同点(通常是 Object
)。这证明了
以下代码:
public class Generics {
public static <T> void print(T what) {
System.out.println(what);
}
public static <T extends Number> void printNumber(T what) {
System.out.println(what);
}
public static void main(String[] args) {
Arrays.stream(Generics.class.getDeclaredMethods())
.filter(m -> m.getName().startsWith("print"))
.forEach(Generics::print);
}
}
这打印:
public static void Generics.print(java.lang.Object)
public static void Generics.printNumber(java.lang.Number)
所以我们可以看到,当它被编译时,它被编译为适用于 Object
的方法。和 Number
分别。
这就是编译和运行这样的东西的原因:
ArrayList<String> list = new ArrayList<>();
list.add("foo");
ArrayList<Object> list2 = (ArrayList<Object>)(Object)list;
list2.add(Integer.valueOf(10));
System.out.println(list2.get(0));
System.out.println(list2.get(1));
如果你尝试,你会看到它打印
foo
10
因此,通过向下/向上转换,我们将 ArrayList<String>
变为进入 ArrayList<Object>
- 如果 ArrayList 实际上将其内容存储在类型为 String[]
的数组中,这是不可能的而不是 Object[]
.
注意尝试做
System.out.println(list.get(0));
System.out.println(list.get(1));
将导致 ClassCastException
.而这恰恰暗示了
编译器会。
看下面的代码:
public static void doThingsWithList() {
ArrayList<String> list = new ArrayList<>();
list.add("");
String s = list.get(0);
print(s);
}
编译时,它变成了这个字节码:
public static void doThingsWithList();
Code:
0: new #11 // class java/util/ArrayList
3: dup
4: invokespecial #12 // Method java/util/ArrayList."<init>":()V
7: astore_0
8: aload_0
9: ldc #13 // String
11: invokevirtual #14 // Method java/util/ArrayList.add:(Ljava/lang/Object;)Z
14: pop
15: aload_0
16: iconst_0
17: invokevirtual #15 // Method java/util/ArrayList.get:(I)Ljava/lang/Object;
20: checkcast #16 // class java/lang/String
23: astore_1
24: aload_1
25: invokestatic #17 // Method print:(Ljava/lang/Object;)V
28: return
正如您在 20
线上看到的那样ArrayList.get
的结果实际上被转换为 String
.
所以泛型只是一种语法糖,它变成了自动类型转换,还有一个额外的好处,即编译器可以使用这种语法糖来检测会导致 ClassCastException
的代码。在运行时。
现在,为什么编译器不能为 String[]
做同样的事情呢?和 Object[]
?难道就不能转吗
public <T> T[] addToNewArrayAndPrint(T item) {
T[] array = new T[10];
array[0] = item;
System.out.println(array[0]);
return array;
}
进入
public <T> T[] addToNewArrayAndPrint(T item) {
Object[] array = new Object[1];
array[0] = item;
System.out.println((T) array[0]);
return array;
}
?
没有。因为那意味着
Arrays.equals(addToNewArray("foo"), new String[]{ "foo" });
将为 false,因为第一个数组的类别为 Object[]
第二个会上课 String[]
.
当然,可以更改 Java,以便所有数组的类型都是 Object[]
并且所有访问都将使用强制转换,就像使用泛型一样。但这会破坏向后兼容性,而使用泛型不会因为 ArrayList<String>
与 ArrayList
具有相同的类.
关于java - 为什么泛型在 Java 中的数组中被删除?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/31335389/