简答:
这是因为泛型是帮助编译器帮助你捕捉类型的元数据
错误,一切都被编译为使用最低公分母
(通常是Object)和类型转换。这不是用数组完成的,因为数组
是他们自己的班级。 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" });
将是错误的,因为第一个数组的类为 Object[],第二个数组的类为 String[]。
当然,Java 可以更改为所有数组的类型为Object[],并且所有访问都将使用强制类型转换,就像使用泛型时一样。但这会破坏向后兼容性,而使用泛型则不会,因为 ArrayList<String> 与 ArrayList 具有相同的类。