刚发现我不太了解的一行代码.
List<String>[] stringLists = new List[1];
Run Code Online (Sandbox Code Playgroud)
这行可以在我的AndroidStudio IDE上传递编译,但会发出警告.它似乎违反了面向对象语言的基本规则:"超类对象可以用子类实例实例化,但反之亦然."
String[] ss = new Object[1]; // won't compile
Run Code Online (Sandbox Code Playgroud)
我们知道List是任何通用List类型的超类型,例如List<String>在这种情况下.由于数组是协变的,所以我认为List []类型是超List<String>[]类型.为什么一个List<String>[]可以实例化List[]?
我认为JLS,§4.8,"原始类型"回答了你为什么赋值有效的实际问题:
原始类型的使用仅允许作为遗留代码兼容性的让步.在将泛型引入Java编程语言之后编写的代码中使用原始类型是非常不鼓励的.未来版本的Java编程语言可能会禁止使用原始类型.
这是为了与pre-generics代码兼容.通常,如果你想要类型安全,你根本不应该使用参数化类型的数组,如果作者写的话会更好:
List<List<String>> lists = new ArrayList<>();
Run Code Online (Sandbox Code Playgroud)
关于违反安全类型规则的假设是错误的.你说这List不是一个子类型List<String>.但是,在Java类型系统中,问题的答案是:"是List子类型List<String>吗?" 既不是"是",也不是"不".这是"这个问题无法回答."
(它也可能是不正确混为一谈"类型"和只有一个"班" List类,但List<String>和List<Object>不同类型的奖励:List<String>是不是一个亚型List<Object>.)
为了更详细地说明正在发生的事情,JLS解释了§5.2"赋值上下文"中变量赋值中允许的转换.值得注意的是,该列表以:
后一个链接到§5.1.9,"未经检查的转换",在一些形式主义解释什么是未经检查的转换后,重申理由(强调我的):
未经检查的转换用于实现在引入泛型类型之前编写的遗留代码与已经过转换以使用泛型的库(我们称之为泛化的过程)的平滑互操作.在这种情况下(最值得注意的是,集合框架的客户端
java.util),遗留代码使用原始类型(例如,Collection而不是Collection<String>).原始类型的表达式作为参数传递给库方法,这些库方法使用与其相应形式参数的类型相同类型的参数化版本.在使用泛型的类型系统下,这样的调用不能显示为静态安全.拒绝此类调用将使大量现有代码无效,并阻止它们使用较新版本的库.反过来,这将阻止图书馆供应商利用通用性.为了防止这种不受欢迎的事件转换,可以将原始类型转换为原始类型引用的泛型类型声明的任意调用.虽然转换是不合理的,但它被视为对实用性的让步.在这种情况下会发出未经检查的警告.
官方的故事确实是从原始类型到参数化类型的未经检查的转换被故意添加到语言中,尽管可能不安全,并且出于兼容性原因仅标记了编译警告.(Java非常努力确保在版本X中编译的代码永远不会在版本X + 1中编译或停止使用.)
| 归档时间: |
|
| 查看次数: |
446 次 |
| 最近记录: |