栏目分类:
子分类:
返回
名师互学网用户登录
快速导航关闭
当前搜索
当前分类
子分类
实用工具
热门搜索
名师互学网 > IT > 面试经验 > 面试问答

泛型到底如何工作?

面试问答 更新时间: 发布时间: IT归档 最新发布 模块sitemap 名妆网 法律咨询 聚返吧 英语巴士网 伯小乐 网商动力

泛型到底如何工作?

Java泛型通过类型擦除来实现,即类型参数仅用于编译和链接,而被擦除以执行。即,编译时间类型和运行时类型之间没有1:1的对应关系。特别是,泛型类型的所有实例共享相同的运行时类:

new ArrayList<Quod>().getClass() == new ArrayList<String>().getClass();

在编译时类型系统中,存在类型参数,并用于类型检查。在运行时类型系统中,不存在类型参数,因此不进行检查。

除了类型转换和原始类型,这将没有问题。强制转换是类型正确性的断言,并将类型检查从编译时推迟到运行时。但是正如我们所看到的,编译时间和运行时类型之间没有1:1的对应关系;类型参数在编译期间被擦除。因此,运行时无法完全检查包含类型参数的强制转换的正确性,并且不正确的强制转换可以成功,这违反了编译时类型系统。Java语言规范将此称为
堆污染

结果,运行时不能依赖类型参数的正确性。但是,它必须强制运行时类型系统的完整性,以防止内存损坏。它通过延迟类型检查直到实际使用泛型引用来完成此操作,此时运行时知道它必须支持的方法或字段,并且可以检查它实际上是声明该字段或方法的类或接口的实例。

这样,回到您的代码示例,我对其进行了稍微简化(这不会改变行为):

ArrayList<Quod> test = new ArrayList<Quod>();ArrayList obj = test; obj.add(new Object());System.out.println(test.get(0));

的声明类型为

obj
原始类型
ArrayList
。原始类型会在编译时禁用类型参数检查。结果,
Object
即使
ArrayList
可能仅将
Quod
实例保存在编译时类型系统中,我们也可以将其传递给其add方法。也就是说,我们已经成功地对编译器说谎并实现了堆污染。

剩下的是运行时类型系统。在运行时类型系统中,ArrayList与type的引用一起使用

Object
,因此将an传递
Object
add
方法是完全可以的。调用
get()
也是如此,它也会返回
Object
。这是有分歧的:在您的第一个代码示例中,您有:

System.out.println(test.get(0));

的编译时间类型

test.get(0)
Quod
,唯一匹配的println方法为
println(Object)
,因此嵌入在类文件中的是该方法的签名。因此,在运行时,我们将传递
Object
println(Object)
方法。完全可以,因此不会引发异常。

在第二个代码示例中,您具有:

System.out.println(test.get(0).toString());

同样,的编译时间类型

test.get(0)
Quod
,但是现在我们正在调用其toString()方法。因此,编译器指定要调用
toString
以(或继承为)类型声明的方法
Quod
。显然,此方法需要
this
指向的一个实例
Quod
,这就是为什么编译器
Quod
在调用该方法之前在字节码中插入一个额外的强制类型转换的原因-
该强制类型转换抛出一个
ClassCastException

也就是说,运行时允许使用第一个代码示例,因为未以特定于

Quod
引用的方式使用引用,但是由于引用被用于访问type方法而拒绝了第二个示例
Quod

就是说,您不应该依赖编译器何时确切地插入此合成强制转换,而应该通过编写类型正确的代码来防止堆污染的发生。需要Java编译器通过在代码可能导致堆污染的情况下发出未经检查的原始警告来帮助您。摆脱警告,您将不必了解那些细节;-)。



转载请注明:文章转载自 www.mshxw.com
本文地址:https://www.mshxw.com/it/570340.html
我们一直用心在做
关于我们 文章归档 网站地图 联系我们

版权所有 (c)2021-2022 MSHXW.COM

ICP备案号:晋ICP备2021003244-6号