为什么传递给 List 参数的 List<Integer> 上的 add(String) 不抛出异常?

Why doesn't add(String) on a List<Integer> passed to a List parameter throw an exception?

为什么可以在下面的代码中插入一个String到一个List<Integer>?我有一个 class 将数字插入整数列表:

public class Main {    
    public static void main(String[] args) {        
        List<Integer> list = new ArrayList<Integer>();
        list.add(2);
        list.add(3);
        list.add(4);

        Inserter inserter = new Inserter();
        inserter.insertValue(list);
        System.out.print(list);        
    }
}

然后我有一个单独的 class,它将 String 插入 List,其中的数字字符串值为 "42":

public class Inserter {    
    void insertValue(List list)
    {
        list.add(new String("42"));
    }
}

当我将 String 添加到整数列表时,为什么编译器不引发编译器错误,或者运行时抛出运行时异常,例如 *CastException?另外,为什么 System.out.print(list) 会产生如下所示的输出而不抛出任何异常?

[2, 3, 4, 42]

让这一切发生的根本原因是什么?

这可能是说明 type erasure 泛型的示例(我建议阅读 link 以充分理解这一点以及它在 Java 泛型中扮演的关键角色)。

  1. list 声明为 List<Integer>
  2. 当它作为参数传递给 listValue 时,它被 转换为 raw type
  3. 从这一点开始,在该方法中 运行 时间的程序不知道它最初是 "list of Integer",因此它可以毫无例外地插入任何对象 - 在 [=53] 中=]声明类型变量被擦除为Object
  4. 在 main 方法中,print 命令只是在列表上调用 toString,它不关心它包含什么,所以它打印包括字符串在内的元素。

如果要查看异常,请尝试添加一行:

Integer myInt = list.get(3); // try to get the string

这将抛出 ClassCastException,因为编译器会在擦除期间在必要时插入转换以保护类型安全。

将参数化类型(例如 List<Integer> 转换为原始类型(例如 List)会给您一个编译器警告,告诉您正是这种问题可能即将发生。您可以使用 @SuppressWarnings("unchecked") (or "rawtypes", if it is available to you) 抑制该警告。这是 "acknowledging" 的一个好方法,表明您将要执行的操作会导致未检查的 运行 时间异常,并且还有助于告诉其他未来的编码员可能会发生一些奇怪的事情。例如:

void insertValue(@SuppressWarnings("unchecked") List list)
{
    list.add(new String("42"));
}

编译器会抛出

Exception in thread "main" java.lang.ClassCastException: java.lang.String cannot be cast to java.lang.Integer 234 at com.performance.Main.main(Main.java:26)

因为您在此处指定类型 ArrayList<Integer>(); 并且当您添加字符串时 list.add(new String("42"));