为什么传递给 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 泛型中扮演的关键角色)。
list
声明为 List<Integer>
- 当它作为参数传递给
listValue
时,它被 转换为 raw type
- 从这一点开始,在该方法中 运行 时间的程序不知道它最初是 "list of Integer",因此它可以毫无例外地插入任何对象 - 在 [=53] 中=]声明类型变量被擦除为
Object
†
- 在 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"));
为什么可以在下面的代码中插入一个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 泛型中扮演的关键角色)。
list
声明为List<Integer>
- 当它作为参数传递给
listValue
时,它被 转换为 raw type - 从这一点开始,在该方法中 运行 时间的程序不知道它最初是 "list of Integer",因此它可以毫无例外地插入任何对象 - 在 [=53] 中=]声明类型变量被擦除为
Object
† - 在 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"));