Warning: file_get_contents(/data/phpspider/zhask/data//catemap/9/visual-studio/7.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
为什么Java对重写的静态方法强制执行返回类型兼容性?_Java_Static_Overriding - Fatal编程技术网

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

为什么Java对重写的静态方法强制执行返回类型兼容性?,java,static,overriding,Java,Static,Overriding,根据和,Java静态方法不是虚拟的,不能被重写。因此,直觉上,这应该是可行的(即使在99%的情况下,这是危险的编程): 然而,在实践中,这会让你: 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 Num

根据和,Java静态方法不是虚拟的,不能被重写。因此,直觉上,这应该是可行的(即使在99%的情况下,这是危险的编程):

然而,在实践中,这会让你:

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() {
                         ^
天真地说,似乎
Foo.frob()
Bar.frob()
应该彼此无关;然而,Java坚持认为它们确实如此。为什么?

(注意:我不想听到为什么用这种方式编码是个坏主意,我想知道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.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() {
                         ^
Foo
Foo
Bar
问题是,为什么它不能(在不兼容签名的情况下)那么容易地让您:


考虑以下几点:

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.doThing
被转换为对
a.doThing
的调用,并且可以被覆盖


这似乎主要是对JLS的判断。JLS解决这一问题的最具体方式似乎是,这并不是说静态方法不会被继承。

这是因为在Java中,特定方法的调用基于对象的运行时类型,而不是编译时类型。但是,静态方法是类方法,因此,在编译时仅使用编译时类型信息来解析对它们的访问。也就是说,如果您可以编译上面的代码并使用这样的代码,会发生什么

Foo bar = new Bar();
bar.frob();
,简而言之:

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


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

好的,JVM可能允许这样做,但是让我们从编译器的角度来讨论为什么这是一个非常糟糕的想法


实例数据(包括实例的类型)不是在编译时要解决的简单问题。显然,它在运行时是众所周知的。如果我有一个bar类型的变量bar,并且我调用s=bar.frob(),编译器将需要对bar的类型进行反向工程,以查看返回值是否可以接受。如果在编译时确定类型是一个非常困难的问题,那么这最多只能使编译器效率低下。在最坏的情况下,答案是错误的,您会得到应该在编译时捕获的运行时错误。

我不确定这一定是最好的决策——它确实会导致类似的尴尬——但这不是一个不合理的决策,我假设他们有我还没有想到的这个判断调用的理由。
myB
将调用
doThing()
关闭
myB
的类型。我的意思是说
myB
属于
B
类型,它没有覆盖
A.doThing()
)@LouisWasserman“运行它!它可以编译”,这就是为什么我们在这里试图说Java不应该允许编译这种东西,我的意思是B.doThing();如果Java是智能的,应该抛出一个编译器错误。就像我说的,有一定数量的判断调用。例如,如果这是禁止的,那么如果
A
具有静态和非静态方法,并且
B扩展了A
,那么您应该能够在
B
中使用
A
的静态方法而无需任何限制吗?这是一个棘手的决定,每个方向都有争论。这并不能回答问题。在您的示例中,我希望
Foo.frob()
被称为
bar
Foo
@SteveKuo,但在这种情况下,正常的Java签名规则起作用;继承的方法不能有相同的参数和不同的返回类型。我不相信这个参数。编译器已对此设置了规则。如果
b
被声明为
Bar
,那么
b.frob()
就是
Bar.frob()
,如果
b
被声明为
Foo
,那么即使
b
实际上是
Bar
的一个实例,
b.frob()
就是
Foo.frob()
。如果创建一个类型兼容的示例,这很容易演示。就JVM而言,它将在继承层次结构中搜索包括返回类型在内的精确匹配。它忽略了“协变返回类型”等等。编译器已经能够解决这个问题了——请参阅我的评论。@DavidMoles那么它只是“因为Java不允许方法具有相同的签名但不同的返回类型。”至于rational,您需要询问Gosling/等等。但大多数都是这样解决的“这是消除特定类型用户错误的简单方法。”
Foo bar = new Bar();
bar.frob();