ArrayList <integer>采用String </integer>

时间:2015-03-24 12:55:20

标签: java generics arraylist collections raw-types

public class Main {
    public static void main(String[] args) {

        ArrayList<Integer> ar = new ArrayList<Integer>();
        List l = new ArrayList();
        l.add("a");
        l.add("b");
        ar.addAll(l);
        System.out.println(ar);
    }
}

输出:[a,b]

您无法直接将String添加到ArrayList<Integer> ar,但可以使用addAll()

我们如何将String添加到ArrayList,其类型已被指定为Integer?任何人都可以突出显示清晰的实现细节及其背后的原因吗?

6 个答案:

答案 0 :(得分:50)

  

但是我们怎样才能在arraylist中添加字符串,其类型已经被指定为Integer?

由于Java泛型的设计是为了向后兼容,基本上是类型擦除和原始类型。

在执行时,没有ArrayList<Integer>这样的东西 - 只有ArrayList。您正在使用原始类型List,因此编译器在编译时或添加执行时转换时不会执行任何正常检查。

编译器确实警告你,你做的是不安全的事情:

Note: Main.java uses unchecked or unsafe operations.
Note: Recompile with -Xlint:unchecked for details.

...当你用相关标志重新编译时,它会警告一切,包括可能是最令人惊讶的一行:

ar.addAll(l);

那是在编译方面让我感到惊讶的那个 - 我相信它实际上相信List确实是Collection<? extends Integer>,当我们知道它不是时。

如果你避免使用原始类型,这种混乱就会消失。

答案 1 :(得分:8)

这更多是关于Java类型系统中的原始类型和泛型类型的混合,而不是类型擦除。让我从问题中增加代码片段:

    ArrayList<Integer> ar = new ArrayList<Integer>();
    List l = new ArrayList();      // (1)
    l.add("a");
    l.add("b");
    ar.addAll(l);                  // (2)
    System.out.println(ar);
    Integer i = ar.get(0);         // (3)

使用今天删除的泛型,第(3)行抛出ClassCastException。如果确定了Java的泛型,那么很容易假设运行时类型检查会导致在第(2)行抛出异常。这将是一个可能的具体设计的设计,但其他设计可能不会做那种检查。为什么不?主要出于同样的原因,我们今天已经删除了泛型:迁移兼容性。

Neal Gafter在他的文章Reified Generics for Java中观察到,有很多不安全的使用泛型,不正确的演员表等等。今天,在仿制药推出十多年后,我仍然看到了很多原型的使用。 (不幸的是,这里包括Stack Overflow。)无条件地执行有效的通用类型检查会破坏巨大的数量的代码,这当然会对兼容性造成重大打击。

任何现实的通用具体化建议都必须在选择加入的基础上提供具体化,例如通过子类型(如Gafter的提案中)或通过注释(Gerakios,Biboudis,Smaragdakis。Reified Type Parameters Using Java Annotations. [PDF] GPSE 2013.),它必须决定如何处理原始类型。完全禁止原始类型似乎完全不切实际。反过来,有效地允许原始类型意味着有办法绕过泛型类型系统。

(这种决定不是轻率的。我见证了类型理论家之间的大喊大叫,其中一人抱怨Java的类型系统 不健全 。一个理论家,这是最严重的侮辱。)

基本上,这就是这段代码的作用:它通过使用原始类型来绕过泛型类型检查的优点。即使Java的泛型已经具体化,也可能不会在第(2)行进行检查。在一些具体化的泛型设计中,代码的行为可能与现在完全相同:在第(3)行抛出异常。

Jon Skeet's answer中,他承认有点惊讶,在第(2)行,编译器信任列表l包含正确类型的元素。这不是真正的信任 - 毕竟,编译器确实在这里发出警告。编译器说的更多的是,“好吧,你使用的是原始类型,你是独立的。如果你后来得到ClassCastException,那不是我的错。”但是,这又是关于允许原始类型用于兼容性目的,而不是擦除。

答案 2 :(得分:7)

您使用的是原始类型。如果您使用List<String> l = new ArrayList<>(),您会发现您的代码将不再编译。原始类型仅用于向后兼容,不应在新代码中使用。

答案 3 :(得分:6)

当它诞生时,Java没有泛型(即,由另一个类参数化的类)。添加泛型时,为了保持兼容性,决定不更改Java字节码和类文件格式。因此,泛型类会被编译器转换为非泛型类。这意味着ArrayList实际上存储了Object类的实例,因此它也可以接受String的实例(即Object的子类)。编译器无法始终检测到误用。

答案 4 :(得分:3)

您遗漏了第二个列表的类型。省略第一个列表的类型,您也可以这样做:

ArrayList ar = new ArrayList();
ar.add(Integer.valueOf(42));
ar.add("Hello");

该类型仅在编译时考虑。这就是为什么你可能会在日食中得到警告。在字节代码中,不考虑类型,并且应用程序在没有异常的情况下运行。

答案 5 :(得分:0)

我们只能在编译期间实现类型安全。在运行时类型橡皮擦将擦除所有这些东西,它将是一个正常的字节代码,即它与没有泛型的情况相同。