为什么静态方法不涉及多态(后期绑定)我看到错误,静态方法不能被覆盖

请考虑以下代码:

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中的:inheritanceTest.B中的m(java.lang.Number)不能覆盖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 

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

JLS§8.4.8.3(Java 8)说:

如果返回类型为R 1的方法声明d 1 覆盖或隐藏另一个返回类型为R 2的方法d 2的声明,那么对于d 2 ,d 1必须是return-type-substitutable(第8.4.5节),或者是编译发生时间错误。

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

由于它是相同的规则,很可能在编译器代码中只有一个地方检查此规则,如果规则被违反,您将收到您正在看到的错误,我确信这是更常见的错误。 编译器确实应该检查它是否应该说“覆盖”或“隐藏”,但看起来它们会滑落。 完全正确地获取错误消息通常不是编译器编写者的最高优先级 – 与确保编译应该编译的代码并运行正确,而不应该编译的代码不相比。 所以我认为这是一个缺陷,但非常小。

即使静态方法无法被覆盖,它们仍然是inheritance的,所以你要做的事情会导致类似的情况

 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 method() Bar method()例如在类似的情况下

 System.out.println(method);//should result be Foo or Bar? 

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

 class X{ List m(){...} } class Y extends X{ LinkedList m(){...} } 

因此, override似乎不是最好的词。 正确的单词应该是hide因为静态方法可以被隐藏,而不是被覆盖,但由于检查重写/隐藏方法的规则相同,因此其错误消息更为通用。

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

语言规范说:

如果具有返回类型R1的方法声明d1覆盖或隐藏具有返回类型R2的另一个方法d2的声明,则d1必须是d2的return-type substitutable,否则会发生编译时错误。

这里你的B方法隐藏了Am的声明:

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

如果你的B类没有m方法,那么你可以调用Bm,它会调用A上定义的m。

让Bm隐藏A的m版本。 因为您可以调用在超类上定义的静态方法,但是引用子类,这会设置对不同返回类型违反的方法的一些期望。

它隐藏而不是覆盖,因为如果你定义了Bm,你仍然可以调用Am并获得该方法的超类版本。 覆盖它的运行时类型决定了被调用的内容以及它的调用方式并不重要。