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

jav*_*ser 8 java generics

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

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);        
    }
}
Run Code Online (Sandbox Code Playgroud)

然后,我有一个单独的类,其插入String到一个List,与所述数字串值"42":

public class Inserter {    
    void insertValue(List list)
    {
        list.add(new String("42"));
    }
}
Run Code Online (Sandbox Code Playgroud)

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

[2, 3, 4, 42]
Run Code Online (Sandbox Code Playgroud)

允许所有这些发生的根本原因是什么?

And*_*own 8

这可能是一个例子来说明泛型的类型擦除(我建议阅读该链接以完全理解它以及它在Java泛型中扮演的关键角色).

  1. list 被宣布为 List<Integer>
  2. 当它作为参数传递给listValue它时,它被转换为原始类型
  3. 从这一点开始,在该方法中,程序在运行时不知道它最初是一个"整数列表",因此它可以插入任何对象,没有异常 - 在类声明中,类型变量被删除为Object
  4. 在main方法中,print命令只调用toString列表,它不介意它包含的内容,因此它打印包含字符串的元素.

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

Integer myInt = list.get(3); // try to get the string
Run Code Online (Sandbox Code Playgroud)

这将抛出一个ClassCastException编译器,在擦除期间,在必要时插入强制转换以保护类型安全.

参数化类型的转换,例如List<Integer>原始类型,例如List,给你一个编译器警告,告诉你这种问题可能即将发生.您可以使用@SuppressWarnings("unchecked")(或者"rawtypes",如果它可用)来抑制该警告.这是一种"承认"你将要做一些会导致未经检查的运行时异常的好方法,并且还有助于告诉其他未来的编码人员可能会发生一些时髦的事情.例如:

void insertValue(@SuppressWarnings("unchecked") List list)
{
    list.add(new String("42"));
}
Run Code Online (Sandbox Code Playgroud)