为什么 Java 对重写的静态方法强制实施返回类型兼容性?

2022-09-01 12:15:55

根据这个答案这个答案,Java静态方法不是虚拟的,不能被覆盖。因此,直观地说,这应该有效(即使在99%的情况下,这是危险的编程):

class Foo
{
    public static String frob() {
        return "Foo";
    }
}

class Bar extends Foo
{
    public static Number frob() {
        return 123;
    }
}

但是,在实践中,这可以帮助您:

Foo.java:10: frob() in Bar cannot override frob() in Foo; attempting to use incompatible return type
found   : java.lang.Number
required: java.lang.String
    public static Number frob() {
                         ^

天真地,它似乎并且不应该彼此无关;然而Java坚持认为他们是这样做的。为什么?Foo.frob()Bar.frob()

(注意:我不想听到为什么以这种方式编码是一个坏主意,我想听听Java和/或JVM设计中是什么使得这种限制成为必要。


更新以添加:对于那些认为编译器会因为在实例上调用静态方法而感到困惑的人,如果你允许这样做:它不会。在方法签名兼容的情况下,它已经必须弄清楚这一点

class Foo
{
    static String frob() {
        return "Foo";
    }
}

class Bar extends Foo
{
    static String frob() {
        return "Bar";
    }
}

class Qux {
    public static void main(String[] args) {
        Foo f = new Foo();
        Foo b = new Bar();
        Bar b2 = new Bar();

        System.out.println(f.frob());
        System.out.println(b.frob());
        System.out.println(b2.frob());
    }
}

获得:

Foo
Foo
Bar

问题是,为什么它不能像不兼容的签名那样容易(在不兼容签名的情况下)让你得到的具体原因是什么:

Foo
Foo
123

答案 1

请考虑以下事项:

public class Foo {
  static class A {
    public static void doThing() {
      System.out.println("the thing");
    }
  }

  static class B extends A {

  }

  static class C extends B {
    public static void doThing() {
      System.out.println("other thing");
    }
  }

  public static void main(String[] args) {
    A.doThing();
    B.doThing();
    C.doThing();
  }
}

运行它!它编译并打印出来

the thing
the thing
other thing

静态方法有点像继承 - 从某种意义上说,它被翻译成对的调用 - 并且可以被重写。B.doThingA.doThing

这似乎主要是对JLS的判断。不过,JLS似乎解决这个问题的最具体方式是第8.2节,它根本没有说静态方法不是继承的。


答案 2

JLS 8.4.2 方法签名,简要介绍:

如果两个方法具有相同的名称和参数类型,则它们具有相同的签名。

关于静态性,什么也没说。静态方法可以通过实例(或空引用)调用 - 如果子类是通过超类声明引用的,该方法应如何解析?


推荐