为什么 javac 允许一些不可能的转换而不是其他的?

Why does javac allow some impossible casts and not others?

如果我尝试将 String 转换为 java.util.Date,Java 编译器会捕获错误。那么,为什么编译器不将以下内容标记为错误?

List<String> strList = new ArrayList<>();                                                                      
Date d = (Date) strList;

当然,JVM 在运行时会抛出 ClassCastException,但编译器不会标记它。

行为与 javac 1.8.0_212 和 11.0.2.

相同

演员在技术上是可行的。 javac 无法轻易证明您的情况并非如此,并且 JLS 实际上将其定义为有效的 Java 程序,因此标记错误是不正确的。

这是因为List是一个接口。因此,您可以将 Date 的子 class 实际实现 List 伪装成 List 在这里 - 然后将其转换为 Date 就完全可以了。例如:

public class SneakyListDate extends Date implements List<Foo> {
    ...
}

然后:

List<Foo> list = new SneakyListDate();
Date date = (Date) list; // This one is valid, compiles and runs just fine

检测这种情况可能并不总是可能的,因为如果实例来自某个方法,则需要运行时信息。即使这样,编译器也需要付出更多的努力。由于 class-tree 根本无法匹配,编译器只会阻止绝对不可能的转换。如所见,这里不是这种情况。

请注意,JLS 要求您的代码是有效的 Java 程序。在 5.1.6.1. Allowed Narrowing Reference Conversion 它说:

A narrowing reference conversion exists from reference type S to reference type T if all of the following are true:

  • [...]
  • One of the following cases applies:
    • [...]
    • S is an interface type, T is a class type, and T does not name a final class.

所以即使编译器能够发现你的案例实际上是不可能的,它也不允许标记错误,因为 JLS 将其定义为有效 Java 程序。

只允许显示警告。

让我们考虑一下您的示例的概括:

List<String> strList = someMethod();       
Date d = (Date) strList;

这些是Date d = (Date) strList;不是编译错误的主要原因。

  • 直觉原因是编译器(通常)不知道该方法调用return对象的精确类型.有可能除了是实现 List 的 class 之外, 也是 Date 的子 class。

  • 技术原因是Java语言规范"allows"缩小引用转换 对应于此类型转换。根据 JLS 5.1.6.1:

    "A narrowing reference conversion exists from reference type S to reference type T if all of the following are true:"

    ...

    5) "S is an interface type, T is a class type, and T does not name a final class."

    ...

    另外一个地方JLS也说了可能会在运行时抛出异常...

    请注意,JLS 5.1.6.1 的确定 涉及变量的声明类型,而不是实际的运行时类型。在一般情况下,编译器不知道也不能知道实际的运行时类型。


那么,为什么 Java 编译器不能计算出转换不工作?

  • 在我的示例中,someMethod 调用可以 return 具有多种类型的对象。即使编译器能够分析方法体并确定可以 returned 的精确类型集,也没有什么可以阻止有人将其更改为 return 不同的类型......在编译调用它的代码。这就是JLS 5.1.6.1说到做到的根本原因。

  • 在您的示例中,智能编译器可以发现转换永远不会成功。并且允许发出编译时警告来指出问题。

那么为什么不允许智能编译器说这是一个错误呢?

  • 因为 JLS 说这是一个有效的程序。时期。任何将其称为 error 的编译器都不符合 Java。

  • 此外,任何拒绝 JLS 和 other 编译器认为有效的 Java 程序的编译器都会妨碍 Java 源代码.

5.5.1. Reference Type Casting:

Given a compile-time reference type S (source) and a compile-time reference type T (target), a casting conversion exists from S to T if no compile-time errors occur due to the following rules.

[...]

If S is an interface type:

  • [...]

  • If T is a class or interface type that is not final, then if there exists a supertype X of T, and a supertype Y of S, such that both X and Y are provably distinct parameterized types, and that the erasures of X and Y are the same, a compile-time error occurs.

    Otherwise, the cast is always legal at compile time (because even if T does not implement S, a subclass of T might).

List<String>SDate 在你的情况下是 T