java - 为什么对隐藏的静态方法强制执行返回类型协变?

标签 java jls

由于 ChildstaticMethodString 返回类型,此代码无法编译。

class Parent {
    static void staticMethod() {    
    }
}

class Child extends Parent {
    static String staticMethod() {
        return null;
    }
}

我知道 §8.4.8.3 中的 JLS 8,“覆盖和隐藏的要求”说:

If a method declaration d1 with return type R1 overrides or hides the declaration of another method d2 with return type R2, then d1 must be return-type-substitutable (§8.4.5) for d2, or a compile-time error occurs.

我的问题是,在静态方法的特定情况下进行这种编译时检查的动机是什么,最好举例说明在编译期间未能进行这种验证会产生任何问题。

最佳答案

这是 Java 中最奇怪的事情之一。假设我们有以下 3 个类

public class A
{
    public static Number foo(){ return 0.1f; }
}

public class B extends A
{
}

public class C
{
    static Object x = B.foo();    
}

假设所有 3 个类都来自具有不同发布时间表的不同供应商。

C的编译时,编译器知道方法B.foo()实际上来自A,签名是foo()->数字。但是,为调用生成的字节代码不引用 A;相反,它引用方法 B.foo()->Number。请注意,返回类型是方法引用的一部分。

当JVM执行这段代码时,它首先在B中寻找方法foo()->Number;当找不到该方法时,将搜索直接父类(super class) A,依此类推。找到并执行 A.foo()

现在魔法开始了 - B 的供应商发布了 B 的新版本,“覆盖” A.foo

public class B extends A
{
    public static Number foo(){ return 0.2f; }
}

我们从 B 那里得到了新的二进制文件,然后再次运行我们的应用程序。 (注意 C 的二进制文件保持不变;它没有针对新的 B 重新编译。) Tada! - C.x 在运行时现在是 0.2f!!因为 JVM 这次搜索 foo()->NumberB 结束。

这个神奇的特性为静态方法增加了一定程度的活力。但老实说,谁需要这个功能?可能没有人。它只会造成困惑,他们希望可以消除它。

请注意,搜索方式仅适用于父链的单链 - 这就是为什么当 Java8 在接口(interface)中引入静态方法时,他们必须决定这些静态方法不被子类型继承。

让我们更深入地了解这个兔子洞。假设 B 发布了另一个版本,具有“协变返回类型”

public class B extends A
{
    public static Integer foo(){ return 42; }
}

据 B 所知,这可以很好地针对 A 进行编译。 Java 允许它,因为返回类型是“协变的”;这个功能比较新; 以前,“覆盖”静态方法必须具有相同的返回类型。

这次 C.x 会是什么?它是 0.1f!因为JVM在B中没有找到foo()->Number;它位于 A 中。 JVM 将 ()->Number()->Integer 视为两种不同的方法,可能是为了支持在 JVM 上运行的一些非 Java 语言。

如果针对这个最新的B 重新编译C,C 的二进制文件将引用B.foo()->Integer;然后在运行时,C.x 将为 42。

现在,B 的供应商在听到所有提示后,决定从 B 中删除 foo,因为“覆盖”静态方法非常危险。我们从 B 获取新的二进制文件,然后再次运行 C(无需重新编译 C)——砰,运行时错误,因为在 B 或 A 中找不到 B.foo()->Integer

这整个困惑表明允许静态方法具有“协变返回类型”是一种设计疏忽,这实际上只适用于实例方法。

更新 - 此功能在某些用例中可能很有吸引力,例如,静态工厂方法 - A.of(..) 返回 A,而 B.of(..) 返回一个更具体的 B。 API 设计者必须小心并推理潜在的危险用法。如果AB来自同一作者,并且用户不能子类化,这种设计是很安全的。

关于java - 为什么对隐藏的静态方法强制执行返回类型协变?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/30875047/

相关文章:

java - 最高温度Mapreduce Java代码中的运行时错误

java - 使用标准 Java 库进行作业调度

java - 是否允许 Java 编译器对静态调用敏感?

java - 最终的非空字段变为空

java - 相互 SSL - 使用 java 作为客户端时,客户端证书链为空

java - 当有连续的​​大写字母时,如何在 Java 中将驼峰式大小写转换为小连字符

Java 正则表达式 : Matching the beginning of a line

java - 线程中的最终字段语义

java - Java 交集类型与接口(interface)类和类型变量的限制

java - "empty statements"可能会(不会)抛出哪些 Java 错误和异常?