San*_*anu 32 java generics collections arraylist raw-types
public class Main {
public static void main(String[] args) {
ArrayList<Integer> ar = new ArrayList<Integer>();
List l = new ArrayList();
l.add("a");
l.add("b");
ar.addAll(l);
System.out.println(ar);
}
}
Run Code Online (Sandbox Code Playgroud)
输出: [a,b]
你不能直接添加String到ArrayList<Integer> ar,但通过使用addAll()是可能的.
我们如何可以添加String到ArrayList其类型已经被指定为Integer?任何人都可以突出显示清晰的实施细节和背后的原因吗
Jon*_*eet 50
但是我们如何在arraylist中添加字符串,其类型已经被指定为Integer?
由于Java泛型的设计是为了向后兼容,基本上是类型擦除和原始类型.
在执行时,没有这样的东西ArrayList<Integer>- 只有一个ArrayList.您正在使用原始类型List,因此编译器在编译时或添加执行时转换时不执行任何正常检查.
编译器会警告您,您正在做不安全的事情:
Note: Main.java uses unchecked or unsafe operations.
Note: Recompile with -Xlint:unchecked for details.
Run Code Online (Sandbox Code Playgroud)
...当你用相关标志重新编译时,它会警告一切,包括可能是最令人惊讶的线:
ar.addAll(l);
Run Code Online (Sandbox Code Playgroud)
这是我吃惊一些,在编制方面的一个-我相信这是有效地相信了List是Collection<? extends Integer>真的,当我们知道它不是.
如果你避免使用原始类型,这种混乱就会消失.
这更多是关于Java类型系统中的原始类型和泛型类型的混合,而不是类型擦除.让我从问题中扩充代码片段:
ArrayList<Integer> ar = new ArrayList<Integer>();
List l = new ArrayList(); // (1)
l.add("a");
l.add("b");
ar.addAll(l); // (2)
System.out.println(ar);
Integer i = ar.get(0); // (3)
Run Code Online (Sandbox Code Playgroud)
随着今天的擦除泛型,第(3)行抛出ClassCastException.如果定义了Java的泛型,则很容易假设运行时类型检查会导致在第(2)行抛出异常.这将是一个可能的具体设计的设计,但其他设计可能不会做那种检查.为什么不?主要出于同样的原因,我们今天已经删除了泛型:迁移兼容性.
Neal Gafter在他的文章Reified Generics for Java中观察到,仿制药有很多不安全的用法,不正确的演员表等等.今天,在仿制药推出十多年后,我仍然看到了很多原型的使用.(包括,不幸的是,这里对堆栈溢出.)无条件地执行具体化的泛型类型检查将打破一个巨大的代码量,这当然会是一个很大的打击兼容性.
任何现实的通用具体化建议都必须在选择加入的基础上提供具体化,例如通过子类型(如Gafter的提案)或通过注释(Gerakios,Biboudis,Smaragdakis.使用Java注释的Reified Type Parameters. [PDF] GPSE 2013 .),它必须决定如何处理原始类型.完全禁止原始类型似乎完全不切实际.反过来,有效地允许原始类型意味着有一种方法来规避泛型类型系统.
(这种决定不是轻率的.我见证了类型理论家之间的呐喊,其中一人抱怨Java的类型系统是不健全的.对于一个类型理论家来说,这是最严重的侮辱.)
从本质上讲,这就是这段代码的作用:它通过使用原始类型来绕过泛型类型检查的优点.即使Java的泛型已经具体化,也可能不会在第(2)行进行检查.在一些具体化的泛型设计中,代码的行为可能与现在完全相同:在第(3)行抛出异常.
在Jon Skeet的回答中,他承认有点惊讶,在第(2)行,编译器信任该列表l包含正确类型的元素.这不是真正的信任 - 毕竟,编译器确实在这里发出警告.编译器说的更多,"好吧,你使用的是原始类型,你可以自己动手.如果你ClassCastException以后再这样,那不是我的错." 但是,这又是关于允许原始类型用于兼容性目的而不是擦除.
当它诞生时,Java没有泛型(即,由另一个类参数化的类).添加泛型时,为了保持兼容性,决定不更改Java字节码和类文件格式.因此,泛型类会被编译器转换为非泛型类.这意味着ArrayList实际上存储了Object类的实例,因此它也可以接受String的实例(即Object的子类).编译器无法始终检测到错误.
| 归档时间: |
|
| 查看次数: |
2213 次 |
| 最近记录: |