java - 为什么泛型在 Java 中的数组中被删除?

标签 java arrays generics

我知道在 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/

相关文章:

java - RESTful Jersey 用户访问指南

java - 查找数组之间的相似值

php - 我是否破坏了以下处理 3 种(人类)语言的 php 数组中的任何 "php good practice"?

c# - 如何通过基类中的方法返回通用类型?

java - 如何拥有一个可滚动的 JTextPane?

Java如何重新排列字符串中的数字

c - 如何在数组中显示多个具有不同十进制长度的数字,C 编程?

c - 如何在C语言中正确使用malloc?

c# - 如果没有 `typedef` ,如何创建在 C# 泛型中使用的相关类型组?

java - 泛型相关问题