将字符串添加到List

以下代码的结果:

public class ListIntegerDemo1 { public static void addToList(List list) {list.add("0067");list.add("bb");} public static void main(String[] args) { List list = new ArrayList(); addToList(list); System.out.println(list.get(0)); } } 

"0067"

我本来期望一个RuntimeException或类似的,因为我正在向整数列表添加一个String。

为什么它没有任何问题?

在运行时,generics类型参数被擦除,因此List变为List ,您可以向其添加任何引用类型。

如果将addToList(List list)更改为addToList(List list) ,编译器将阻止您在此方法中将字符串添加到该列表。

为什么不抛出exception有两个原因,这取决于我们如何看待这个特定的例子。 还取决于我们认为应该抛出exception的原因或位置 。 (如果我们认为应该抛弃一个,那么很明显Java会另外考虑。)

问题中的代码很棘手,即使我们知道为什么add不会抛出exception,因为一个原因与Java选择重载的方式有关。 我建议运行以下程序以更好地理解:

 class Example { static void m(int arg) { System.out.println("int " + arg); } static void m(Object arg) { System.out.println("Object " + arg); } public static void main(String[] args) { m(new Integer(0)); } } 

这打印Object 0 。 为什么? 因为Java在三个阶段中搜索过载:*

  1. 第一阶段寻找过载而不允许装箱,拆箱和变速器。
  2. 第二阶段寻找允许装箱和拆箱的过载。
  3. 第三阶段寻找允许装箱,拆箱和变速器的过载。

这些阶段是“短路”,因此在m(new Integer(0))的情况下,阶段1在考虑需要拆箱的int重载之前找到Object重载。 由于发现过载,因此不尝试阶段2。

ListIntegerDemo1发生同样的事情,其中​​选择了System.out.println(Object)

generics被删除 ,这意味着:

  • list.get实际上返回Object
  • 要将其结果分配给Integer ,编译器会像(Integer) list.get(0)一样插入一个(Integer) list.get(0)

但是不需要插入强制转换来调用Object重载,因此不抛出exception。

使用原始类型 ListString添加到List称为堆污染 。 问题中的代码是为什么堆污染如此糟糕的一个例子:因为有时我们没有发现它。 有时我们会在很久之后发现它并且不知道错误实际上在哪里。

如果我们这样做,将抛出exception:

 Integer i = list.get(0); System.out.println(i); 

因为在这种情况下,需要演员来完成作业。

如果我们这样做:

 public class ListIntegerDemo1 { public static void addToList(List list) {list.add(67);list.add(0xbb);} public static void main(String[] args) { List list = new ArrayList<>(); addToList(list); System.out.println(list.get(0)); } } 

因为在这种情况下, System.out.println(String)被选择为更具体,并插入了强制转换。

我们还可以通过使用Collections#checkedList来阻止我们的List在不安全的代码中被污染:

 List list = Collections.checkedList( new ArrayList<>(), Integer.class); 

在这种情况下,将在add addToList内部的调用中抛出exception。

当然最好的解决方案是不使用原始类型:

 static void addToList(List list) { list.add("0067"); list.add("bb"); } 

现在我们无法将List传递给该方法。

addToList也可以接受List List


*详见15.12.2 。