当前位置: 首页 > 知识库问答 >
问题:

为什么若静态方法不涉及多态性(后期绑定),我会看到静态方法不能被重写的错误

温嘉赐
2023-03-14

请考虑以下代码:

class A{
    public static void m(Number n){
         System.out.println("Number A");
    };
}
class B extends A{
     public static int m(Number n){
        System.out.println("Number B");
        return 1;
      };
}

输出:

继承测试中的java:m(java.lang.Number)。B无法在inheritanceTest中重写m(java.lang.Number)。返回类型int与void不兼容

我知道静态方法不涉及多态性,因此我推断我的代码不可能重写。这个编译器消息对我来说很奇怪。

据我所知,重写是多态性的一部分。我准备scjp,我害怕在熟悉的问题上犯错误。

请澄清这个问题。

我的预期行为-有关重载错误的消息

P.S1。

我读过关于静态重写的热门问题,但并没有找到答案(

P. S2.根据Pshemo的回答:

此代码:

class Foo{
    public static void m(Number n){
         System.out.println("Number A");
    };
    public static int m(Number n){
        System.out.println("Number B");
        return 1;
    };
}

产出:

error: method m(Number) is already defined in class Foo
    public static int m(Number n){
                      ^
1 error

对我来说,这些情况都是一样的。但编译器错误是不同的——奇怪。

共有3个答案

杜俊远
2023-03-14

我认为“override”的编译器错误用法在这里有误导性,它不适用。

语言规范说:

如果返回类型为R1的方法声明d1重写或隐藏另一个返回类型为R2的方法d2的声明,则d1必须是可替换为d2的返回类型,否则会发生编译时错误。

这里,您的B方法隐藏了A.m的声明:

如果一个类声明了一个静态方法m,那么声明m被称为隐藏任何方法m',其中m的签名是m'签名的子签名(§8.4.2),该方法位于该类的超类和超接口中,否则该类中的代码可以访问该方法。

如果您的B类没有m方法,那么您可以调用B.m,它将调用A上定义的m。

拥有B. m就是隐藏A版本的m。因为您可以调用在超类上定义的静态方法,但引用子类,这就设置了一些关于不同返回类型违反的方法的期望。

它是隐藏的,而不是重写的,因为如果您定义了一个B.m,您仍然可以调用a.m并获取该方法的超类版本。通过重写,由运行时类型决定调用什么以及如何调用并不重要。

蒋波光
2023-03-14

即使静态方法不能被重写,它们仍然是继承的,所以你试图做的事情会导致类似于

class Foo{
    public static void m(Number n){
         System.out.println("Number A");
    };
    public static int m(Number n){
        System.out.println("Number B");
        return 1;
    };
}

这是错误的,因为不能有两个具有相同签名但具有不同返回类型的方法。它被禁止的原因很简单。。。假设我们有一些方法:

  • Foo方法(){返回新的Foo();}
  • Bar方法(){返回新的Bar();}

你会希望像这样调用它们

System.out.println(method());

结果应该是Foo还是Bar?编译器无法决定。

为了防止这种情况,编译器禁止通过更改其返回类型来重写/隐藏具有相同签名的方法。唯一的例外是当您将返回类型更改为更详细的返回类型时,如

class X{
    List<String> m(){...}
}

class Y extends X{
    LinkedList<String> m(){...}
}

所以似乎重写在这里不是最好的词。正确的单词应该是隐藏,因为静态方法可以隐藏,而不是重写。但是看起来相同的规则(或者至少其中的一部分)被用来测试我们是否可以隐藏方法作为重写的规则,所以在出现问题的情况下,会显示相同的错误消息(关于重写而不是隐藏),这可能会误导。

蒋向笛
2023-03-14

JLS§8.4.8.3(Java 8)规定:

如果返回类型为R1的方法声明d1覆盖或隐藏另一个返回类型为R2的方法声明d2,则d1对于d2必须是可替换的返回类型(§8.4.5),否则会发生编译时错误。

同样的规则也适用于实例方法和静态方法,因为它表示“重写或隐藏”。基本上,如果您有一个具有相同名称和相同参数的方法,如果它是实例方法,它将覆盖,但是如果它是类(静态)方法,它将隐藏(继承的方法)。在这两种情况下,返回类型必须相同或遵守协方差规则。

因为它是相同的规则,很可能在编译器代码中只有一个地方检查这个规则,如果规则被违反,你会看到错误,我相信这是更常见的。编译器真的应该检查一下它是否应该说“覆盖”或“隐藏”,但是看起来它们滑了。获得正确的错误消息通常不是编译器编写者的最高优先级——相比之下,确保应该编译的代码这样做并正确运行,而不应该编译的代码则不正确。所以我认为这是一个不足,但非常小的不足。

 类似资料:
  • 编译器在第3行给出了一个错误 此实例方法无法重写Abc中的静态方法 为什么静态方法不能被实例方法覆盖?

  • 伙计们,我有一个简单但令人讨厌的问题。据我所知,基本上意味着,对于该类的每个实例,这个方法将是相同的,如果我们改变它,这将改变该类的每个实例,它也被称为类方法。那么,如果我有一个类,它实现了某种格式的方法,让我们说: 为什么不能设置为静态?因为该类的每个实例的格式都是相同的。。。?

  • 输出为"A的静态方法"。因此静态方法不会被重写,否则输出将是“静态方法B”。JVM如何在运行时决定调用类A而不是B的静态方法。

  • 问题内容: 在静态上下文中,为什么不能调用的静态版本(而不是必须使用)? 编译器不够聪明,无法确定何时使用对象方法+何时使用静态方法吗? 注意: 我并不是说应该使用a 而不 是非静态方法(这很明显- 如果是的子类,则a的of 可能返回或其他,必须在运行时确定)。 我是说我想知道为什么没有 两个 版本,一个是仅适用于静态上下文的静态方法,另一个是常规的非静态方法。如果不可能,那就不可能了,那就是答案

  • 根据我的理解,如果子例程不作用于类的实例(其作用仅限于显式输入/输出),则它是;如果子例程作用于类的实例,则它是(它可能会对实例产生副作用,使其不纯)。 关于这个话题,这里有一个很好的讨论。请注意,根据接受答案的定义,静态实际上应该是一个函数,因为实例从不隐式传递,而且它不能访问任何实例的成员。 不过,考虑到这一点,静态实际上不应该是函数吗? 我想确保我使用了正确的措辞。 有人能澄清一下吗?

  • 我能够运行以下代码: 现在我可以从类B的实例访问display()方法,那么为什么说静态方法不能继承呢。如果我在类B中声明一个方法display,那么可以说超类中的方法被隐藏,子类中的方法被调用,那么这不是重写方法时所需要的行为。