关于Java中的通用数组创建

刚发现一行我不太了解的代码。

List[] stringLists = new List[1]; 

这行可以在我的AndroidStudio IDE上通过编译,但有警告。 它似乎违反了面向对象语言的基本规则:“超类对象可以用子类实例实例化,但反之亦然。”

 String[] ss = new Object[1]; // won't compile 

我们知道List是任何通用List类型的超类型,例如在这种情况下为List 。 并且由于数组是协变的,所以我认为List []类型是List[]类型的超类。 为什么List[]可以用List[]实例化?

我认为JLS,§4.8,“Raw Types”回答了你为什么赋值有效的实际问题:

原始类型的使用仅允许作为遗留代码兼容性的让步。 在将generics引入Java编程语言之后编写的代码中使用原始类型是非常不鼓励的。 未来版本的Java编程语言可能会禁止使用原始类型。

这是为了与pre-generics代码兼容。 一般来说,如果你想要类型安全,你根本不应该使用参数化类型的数组,如果作者写道,它会更好:

 List> lists = new ArrayList<>(); 

关于违反安全类型规则的假设是错误的。 你说List不是List的子类型。 但是,在Java类型系统中,问题的答案是:“ ListList的子类型吗?” 既不是“是”,也不是“不”。 这是“这个问题无法回答。”

(混合“类型”和“类”也可能不正确。只有一个List类,但ListList是不同的类型.Bonus: List 不是 List的子类型List 。)


为了更多地详细说明正在发生的事情,JLS解释了§5.2“赋值上下文”中变量赋值中允许的转换。 值得注意的是,该列表以:

如果在应用上面列出的转换后,结果类型是原始类型( §4.8 ),则可以应用未经检查的转换(第5.1.9节 )。

后一个链接到§5.1.9,“未经检查的转换” ,在一些forms主义解释什么是未经检查的转换后,重申理由(强调我的):

未经检查的转换用于实现在引入generics类型之前编写的遗留代码与已经过转换以使用generics的库(我们称之为泛化的过程) 的平滑互操作 。 在这种情况下(最值得注意的是, java.util中的Collections Framework的客户端),遗留代码使用原始类型(例如Collection而不是Collection )。 原始类型的表达式作为参数传递给库方法,这些库方法使用相同类型的参数化版本作为其相应forms参数的类型。

在使用generics的类型系统下,这样的调用不能显示为静态安全。 拒绝此类调用将使大量现有代码无效,并阻止它们使用较新版本的库。 反过来,这会阻止图书馆供应商利用通用性。 为了防止这种不受欢迎的事件转换,可以将原始类型转换为原始类型引用的generics类型声明的任意调用。 虽然转换不健全,但可以容忍它是对实用性的让步。 在这种情况下会发出未经检查的警告。

官方的故事确实是从原始类型到参数化类型的未经检查的转换被故意添加到语言中,尽管可能不安全,并且出于兼容性原因仅标记了编译警告。 (Java非常努力确保在版本X中编译的代码永远不会在版本X + 1中编译或停止使用。)

Java中的generics只是编译时的语法糖。 在执行时,两个列表都是List

我在Eclipse中编译

 Type safety: The expression of type List[] needs unchecked conversion to conform to List[] 

这是正确的警告。 试

 List[] sl = new ArrayList[1]; 

我明白了

 Cannot create a generic array of ArrayList 

在您的代码中,您可以实例化List非generics数组,并使用未经检查的转换将其分配给List[]

这行可以在我的AndroidStudio IDE上通过编译,但有警告。 它似乎违反了面向对象语言的基本规则:“超类对象可以用子类实例实例化,但反之亦然。”

这条线:

 List[] stringLists = new List[1]; 

在我看来,上面不应该编译。 承认Java中的数组是协变的,但我认为结论List是其原始类型的超类是不合理的。

我能够在JLS中找到这个,第4.3节:

Java编程语言中存在上下文,其中使用generics类或接口名称而不提供类型参数。 这种情况不涉及原始类型的使用(§4.8)。 相反,它们是类型参数对于generics类或接口的含义不必要或不相关的上下文。

也许,出于上述赋值的目的,允许原始类型,因为“类型参数对于……generics类或接口的含义是不必要的”。 将原始类型分配给stringLists ,它必须是List. 不可否认,如果是真的。