2017-01-03 64 views
2

有两种方案 为什么第一个代码代码的工作?我希望它抛出一个运行时异常,在访问元素作为字符串添加的,而不是整数泛型在运行时

同样.. 第二码在访问元素时抛出运行时异常,虽然它能够在arrayList中尽可能舒适地添加Integer,尽管声明它是为了保持String。

在这两个代码,我们成功地将不同的数据类型,但似乎在访问元素

import java.util.ArrayList; 

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

     ArrayList<Integer> arrayList = new ArrayList<>(); 
     Test.addToList(arrayList); 

     System.out.println(arrayList.get(0)); 
    } 

    public static void addToList(ArrayList arrayList) { 
     arrayList.add("i"); 

    } 
} 






import java.util.ArrayList; 

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

     ArrayList<String> arrayList = new ArrayList<>(); 
     Test.addToList(arrayList); 

     System.out.println(arrayList.get(0)); 
    } 

    public static void addToList(ArrayList arrayList) { 
     arrayList.add(1); 

    } 
} 
+1

为什么允许'add'是因为函数定义 - 你定义的函数不知道'arrayList'期望(或应该允许)什么类型 - 至于运行时错误:'String'是一个类,所以ArrayList存储引用(有点类似地址) - 如果你试图访问内存位置1,它不包含有效的String对象,所以你得到运行时错误 – UnholySheep

+0

你应该在运行第二个时得到一个'ClassCastException' 。 – NewUser

+1

@UnholySheep但我不确定你的评论解释了为什么第一个也不会失败。 –

回答

1

泛型在编译过程中只存在出现的问题。它们在编译后从二进制代码中移除,这被称为'类型擦除'。主要是为了向后兼容的原因。

如果编译时没有警告,也没有手动转换,则不可能滥用泛型,因为这会导致编译器错误和一些警告。

当你声明你期望ArrayList为你的功能,没有任何泛型的迹象。您禁用所有编译时检查。你应该得到这个警告。在这种情况下,凡是使用通用参数的地方,只会接受Object

但是,当您使用get()访问对象时,会发生一些隐藏事件,即演员阵容。

ArrayList<String> list = new ArrayList<>(); 
list.add("Hello"); 
String s = list.get(0); 

最后一行被改写为:

String s = (String) list.get(0); 

如果列表中不返回String类型的东西,就会失败。 当你只在列表中使用泛型时,它只会有String(或者你的代码不能编译)。但是,如果您允许将非String对象放入列表中,则强制类型检查将失败。

3

由于type erasure的原因,您可以在两种情况下添加元素。在运行时,班级不知道它被宣布为new ArrayList<String>(),只是它被宣布为new ArrayList()

println的情况下,该方法的编译时重载解析起作用。 System.outPrintStream,它有几个过载println。其中包括:

... 
void println(Object x); 
void println(String x); 

Java编译器将挑选哪些是最具体的。在ArrayList<Integer>的情况下,这是第一个,在ArrayList<String>的情况下,这是第二个。一旦这样做,作为类型擦除处理的一部分,它会将原始ArrayList::get(int)调用的对象结果强制转换为所需的类型,但前提是该强制转换是必需的。

在调用ArrayList<Integer>的情况下,转换不是必需的(ArrayList::get(int)返回一个Object,这正是该方法所期望的),所以javac省略了它。在拨打ArrayList<String>的情况下,它需要,所以javac增加了它。你看到的是:

System.out.println(arrayList.get(0)); 

实际上被编译为:

System.out.println((String) arrayList.get(0)); 

但元素不是字符串,这就是在抛出ClassCastException什么样的结果。