Per this answer和this answer,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() {
^
天真地看来,Foo.frob()
和Bar.frob()
似乎彼此无关;然而Java坚持认为他们这样做了。为什么呢?
(Nb:我不想听到为什么以这种方式编码是个坏主意,我想听听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
答案 0 :(得分:7)
请考虑以下事项:
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似乎解决此问题的最具体方式是section 8.2,这并不是说静态方法不是继承的。
答案 1 :(得分:3)
JLS 8.4.2 Method Signature,简要说明:
如果两个方法具有相同的名称和参数类型,则它们具有相同的签名。
没有任何关于静态的说法。可以通过实例(或空引用)调用静态方法 - 如果通过超类声明引用子类,该方法应如何解决?
答案 2 :(得分:2)
这是因为在Java中,基于对象的运行时类型调用特定方法,而不是基于它的编译时类型。但是,静态方法是类方法,因此只能使用编译时类型信息在编译期间解析对它们的访问。也就是说,如果您可以编译上面的内容并使用像这样的代码
会发生什么Foo bar = new Bar();
bar.frob();
答案 3 :(得分:1)
好吧,JVM可能会允许这样做,但让我们从编译器的角度讨论为什么它是一个非常糟糕的主意。
实例数据(包括实例所在的类型)不是在编译时要解决的简单问题。显然它在运行时是众所周知的。如果我有一个Bar类型的变量条,并且我调用s = bar.frob(),则编译器需要对哪个类型条进行反向工程以查看返回值是否可接受。如果在编译时确定类型是一个超级难题,这会使编译器效率最低。在最坏的情况下,答案是错误的,并且您得到应该在编译时捕获的运行时错误。