将Y添加到Collection <X>,将Y添加为X的instanceof为什么不抛出运行时异常?

问题描述

以下方法任何内容添加任何集合中。为什么没有抛出运行时异常?

public static <T> void addAnything(Collection<? super T> c,Object t) {
    c.add((T) t);
}

例如,

List<String> list = new ArrayList<>();
addAnything(list,"ok");
addAnything(list,2);
addAnything(list,true);
addAnything(list,new Object())
addAnything(list,new Anything())

为什么这段代码中没有抛出任何异常?

解决方法

两个原因:向后兼容性和性能。

为了与旧的不安全代码向后兼容,Java中的泛型通过“类型擦除”实现。类型系统仅在编译时强制执行。当您使用类型强制转换时,您会告诉编译器“这很好,我知道我在做什么”,您只会收到警告。

在运行时添加对象时检查对象的类型将导致性能下降。目前,ArrayList.add不需要了解有关所添加对象的任何信息:它仅适用于对象 reference 。这意味着不需要将对象本身从主存储器加载到CPU寄存器或缓存中。如果必须检查对象的类型,则 cache misses会急剧增加,这会使程序变慢。

如果要确保只添加字符串的列表,可以使用Collections.checkedList方法:

List<String> list = Collections.checkedList(new ArrayList<>(),String.class);
addAnything(list,2); // run time error
,

因为编写的方法旨在将Object类型的实例添加到T的任何超类型的列表中(并且您肯定知道,对于任何T,Object都是T的超类型)。因此,该方法将List<String>视为List<Object>,并向其添加Object实例。这显然是有效的行为。如果您将非字符串对象添加到列表中并尝试对其内容进行处理,则会看到异常。

,

运行时不会由于type erasure引发异常。基本上,您的ArrayList<String>就像非泛型的ArrayList一样被编译,这意味着您可以向其中添加任何对象。

,

为什么添加过程中没有ClassCastException?…为什么这段代码中没有抛出任何异常?…

因为generics are checked at compile-time

您的代码就像the code in this classic example。就像它说的那样:“ 尽管代码编译没有错误,但它会抛出运行时异常 java.lang.ClassCastException ...”