将字符串添加到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
,编译器将阻止您在此方法中将字符串添加到该列表。
为什么不抛出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在三个阶段中搜索过载:*
- 第一阶段寻找过载而不允许装箱,拆箱和变速器。
- 第二阶段寻找允许装箱和拆箱的过载。
- 第三阶段寻找允许装箱,拆箱和变速器的过载。
这些阶段是“短路”,因此在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。
使用原始类型 List
将String
添加到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 super String>
List super String>
。
*详见15.12.2 。