我觉得JLS,§4.8,“原始类型” https://docs.oracle.com/javase/specs/jls/se8/html/jls-4.html#jls-4.8回答你的实际问题why分配有效:
仅允许使用原始类型作为对遗留代码兼容性的让步。强烈建议不要在 Java 编程语言中引入泛型之后编写的代码中使用原始类型。 Java 编程语言的未来版本可能会禁止使用原始类型。
它是为了与预泛型代码兼容。一般来说,如果你想要类型安全,你根本不应该使用参数化类型的数组,如果作者写道:
List<List<String>> lists = new ArrayList<>();
您关于违反类型安全规则的假设是错误的。你是这么说的List
不是 的子类型List<String>
。然而,在 Java 类型系统中,问题的答案是:“是List
的一个子类型List<String>
?”既不是“是”,也不是“否”,而是“这个问题无法回答”。
(It's also probably not correct to conflate "types" and "classes." There is only one List
class, but List<String>
and List<Object>
are different types. Bonus: List<String>
is not a subtype of List<Object>
.)
为了添加有关所发生情况的更多详细信息,JLS 解释了变量赋值中允许进行哪些转换§5.2,“赋值上下文” https://docs.oracle.com/javase/specs/jls/se8/html/jls-5.html#jls-5.2。值得注意的是,该列表以以下内容结尾:
如果在应用上面列出的转换后,结果类型是原始类型(§4.8 https://docs.oracle.com/javase/specs/jls/se8/html/jls-4.html#jls-4.8),未经检查的转换(§5.1.9 https://docs.oracle.com/javase/specs/jls/se8/html/jls-5.html#jls-5.1.9)然后可以应用。
后者链接到§5.1.9,“未经检查的转换” https://docs.oracle.com/javase/specs/jls/se8/html/jls-5.html#jls-5.1.9在一些形式主义解释了什么是未经检查的转换之后,重申了基本原理(强调我的):
使用未经检查的转换来实现遗留代码的平滑互操作,在引入泛型类型之前编写,其中的库已经经过转换以使用泛型(我们称之为泛型化的过程)。在这种情况下(最值得注意的是,Collections Framework 的客户java.util
),遗留代码使用原始类型(例如Collection
代替Collection<String>
)。原始类型的表达式作为参数传递给库方法,这些方法使用这些相同类型的参数化版本作为其相应形式参数的类型。
在使用泛型的类型系统下,此类调用不能被证明是静态安全的。拒绝此类调用将使大量现有代码无效,并阻止它们使用较新版本的库。这反过来又会阻止库供应商利用通用性。为了防止这种不受欢迎的事件发生,原始类型可以转换为原始类型所引用的泛型类型声明的任意调用。虽然这种转变并不健全,但作为对实用性的让步,它是可以容忍的。在这种情况下,会发出未经检查的警告。
官方的说法确实是,从原始类型到参数化类型的未经检查的转换被故意添加到语言中,尽管可能不安全,并且出于兼容性原因仅标记有编译警告。 (Java 非常努力确保在版本 X 中编译的代码永远不会在版本 X+1 中编译失败或不再有用。)