为什么不可能重写静态方法?
如果可能,请举例说明。
为什么不可能重写静态方法?
如果可能,请举例说明。
当前回答
方法重写可以通过动态调度实现,这意味着对象的声明类型不决定其行为,而是决定其运行时类型:
Animal lassie = new Dog();
lassie.speak(); // outputs "woof!"
Animal kermit = new Frog();
kermit.speak(); // outputs "ribbit!"
尽管lassie和kermit都声明为Animal类型的对象,但它们的行为(method .speak())会有所不同,因为动态调度只会在运行时将方法调用.speak()绑定到实现,而不是在编译时。
现在,这里是静态关键字开始有意义的地方:单词“静态”是“动态”的反义词。所以你不能重写静态方法的原因是因为静态成员上没有动态分派——因为静态字面上的意思是“非动态的”。如果它们是动态分派的(因此可以被重写),静态关键字就没有意义了。
其他回答
其实我们错了。 尽管Java默认情况下不允许重写静态方法,但如果你彻底查看Java中Class和Method类的文档,你仍然可以通过以下工作方法来模拟静态方法重写:
import java.lang.reflect.InvocationTargetException;
import java.math.BigDecimal;
class RegularEmployee {
private BigDecimal salary = BigDecimal.ONE;
public void setSalary(BigDecimal salary) {
this.salary = salary;
}
public static BigDecimal getBonusMultiplier() {
return new BigDecimal(".02");
}
public BigDecimal calculateBonus() {
return salary.multiply(this.getBonusMultiplier());
}
public BigDecimal calculateOverridenBonus() {
try {
// System.out.println(this.getClass().getDeclaredMethod(
// "getBonusMultiplier").toString());
try {
return salary.multiply((BigDecimal) this.getClass()
.getDeclaredMethod("getBonusMultiplier").invoke(this));
} catch (IllegalAccessException e) {
e.printStackTrace();
} catch (IllegalArgumentException e) {
e.printStackTrace();
} catch (InvocationTargetException e) {
e.printStackTrace();
}
} catch (NoSuchMethodException e) {
e.printStackTrace();
} catch (SecurityException e) {
e.printStackTrace();
}
return null;
}
// ... presumably lots of other code ...
}
final class SpecialEmployee extends RegularEmployee {
public static BigDecimal getBonusMultiplier() {
return new BigDecimal(".03");
}
}
public class StaticTestCoolMain {
static public void main(String[] args) {
RegularEmployee Alan = new RegularEmployee();
System.out.println(Alan.calculateBonus());
System.out.println(Alan.calculateOverridenBonus());
SpecialEmployee Bob = new SpecialEmployee();
System.out.println(Bob.calculateBonus());
System.out.println(Bob.calculateOverridenBonus());
}
}
输出结果:
0.02
0.02
0.02
0.03
我们想要达到的目标:)
即使我们将第三个变量Carl声明为regulareemployee并给它分配了SpecialEmployee实例,我们仍然会在第一种情况下调用regulareemployee方法,在第二种情况下调用SpecialEmployee方法
RegularEmployee Carl = new SpecialEmployee();
System.out.println(Carl.calculateBonus());
System.out.println(Carl.calculateOverridenBonus());
看看输出控制台:
0.02
0.03
;)
Here is a simple explanation. A static method is associated with a class while an instance method is associated with a particular object. Overrides allow calling the different implementation of the overridden methods associated with the particular object. So it is counter-intuitive to override static method which is not even associated with objects but the class itself in the first place. So static methods cannot be overridden based on what object is calling it, it will always be associated with the class where it was created.
I like and double Jay's comment (https://stackoverflow.com/a/2223803/1517187). I agree that this is the bad design of Java. Many other languages support overriding static methods, as we see in previous comments. I feel Jay has also come to Java from Delphi like me. Delphi (Object Pascal) was one of the languages implementing OOP before Java and one of the first languages used for commercial application development. It is obvious that many people had experience with that language since it was in the past the only language to write commercial GUI products. And - yes, we could in Delphi override static methods. Actually, static methods in Delphi are called "class methods", while Delphi had the different concept of "Delphi static methods" which were methods with early binding. To override methods you had to use late binding, declare "virtual" directive. So it was very convenient and intuitive and I would expect this in Java.
Yes. Practically Java allows overriding static method, and No theoretically if you Override a static method in Java then it will compile and run smoothly but it will lose Polymorphism which is the basic property of Java. You will Read Everywhere that it is not possible to try yourself compiling and running. you will get your answer. e.g. If you Have Class Animal and a static method eat() and you Override that static method in its Subclass lets called it Dog. Then when wherever you Assign a Dog object to an Animal Reference and call eat() according to Java Dog's eat() should have been called but in static Overriding Animals' eat() will Be Called.
class Animal {
public static void eat() {
System.out.println("Animal Eating");
}
}
class Dog extends Animal{
public static void eat() {
System.out.println("Dog Eating");
}
}
class Test {
public static void main(String args[]) {
Animal obj= new Dog();//Dog object in animal
obj.eat(); //should call dog's eat but it didn't
}
}
Output Animal Eating
According to Polymorphism Principle of Java, the Output Should be Dog Eating. But the result was different because to support Polymorphism Java uses Late Binding that means methods are called only at the run-time but not in the case of static methods. In static methods compiler calls methods at the compile time rather than the run-time, so we get methods according to the reference and not according to the object a reference a containing that's why You can say Practically it supports static overring but theoretically, it doesn't.
简短的回答是:这是完全可能的,但Java没有做到。
下面是一些代码,说明了Java中的当前状态:
文件Base.java:
package sp.trial;
public class Base {
static void printValue() {
System.out.println(" Called static Base method.");
}
void nonStatPrintValue() {
System.out.println(" Called non-static Base method.");
}
void nonLocalIndirectStatMethod() {
System.out.println(" Non-static calls overridden(?) static:");
System.out.print(" ");
this.printValue();
}
}
文件Child.java:
package sp.trial;
public class Child extends Base {
static void printValue() {
System.out.println(" Called static Child method.");
}
void nonStatPrintValue() {
System.out.println(" Called non-static Child method.");
}
void localIndirectStatMethod() {
System.out.println(" Non-static calls own static:");
System.out.print(" ");
printValue();
}
public static void main(String[] args) {
System.out.println("Object: static type Base; runtime type Child:");
Base base = new Child();
base.printValue();
base.nonStatPrintValue();
System.out.println("Object: static type Child; runtime type Child:");
Child child = new Child();
child.printValue();
child.nonStatPrintValue();
System.out.println("Class: Child static call:");
Child.printValue();
System.out.println("Class: Base static call:");
Base.printValue();
System.out.println("Object: static/runtime type Child -- call static from non-static method of Child:");
child.localIndirectStatMethod();
System.out.println("Object: static/runtime type Child -- call static from non-static method of Base:");
child.nonLocalIndirectStatMethod();
}
}
如果你运行这个(我在Mac上做的,从Eclipse,使用Java 1.6),你会得到:
Object: static type Base; runtime type Child.
Called static Base method.
Called non-static Child method.
Object: static type Child; runtime type Child.
Called static Child method.
Called non-static Child method.
Class: Child static call.
Called static Child method.
Class: Base static call.
Called static Base method.
Object: static/runtime type Child -- call static from non-static method of Child.
Non-static calls own static.
Called static Child method.
Object: static/runtime type Child -- call static from non-static method of Base.
Non-static calls overridden(?) static.
Called static Base method.
在这里,唯一可能令人惊讶的情况(也是这个问题所涉及的)似乎是第一种情况:
运行时类型并不用于确定调用哪些静态方法,即使是在使用对象实例(obj.staticMethod())调用时也是如此。
最后一种情况:
当从类的对象方法中调用静态方法时,选择的静态方法是类本身可访问的方法,而不是定义对象运行时类型的类可访问的方法。
使用对象实例调用
静态调用在编译时解析,而非静态方法调用在运行时解析。注意,尽管静态方法是继承的(从父方法),但它们不会被(子方法)覆盖。如果你另有期待,这可能会是一个惊喜。
从对象方法中调用
对象方法调用使用运行时类型解析,但静态(类)方法调用使用编译时(已声明)类型解析。
改变规则
要更改这些规则,以便示例中的最后一个调用Child.printValue(),静态调用必须在运行时提供类型,而不是编译器在编译时使用对象(或上下文)声明的类解析调用。然后,静态调用可以使用(动态)类型层次结构来解析调用,就像现在的对象方法调用一样。
这是很容易做到的(如果我们改变Java:-O),并不是完全不合理的,然而,它有一些有趣的考虑。
主要需要考虑的是,我们需要决定哪个静态方法调用应该执行此操作。
目前,Java语言中有这种“怪癖”,即obj.staticMethod()调用被ObjectClass.staticMethod()调用取代(通常带有警告)。[注意:ObjectClass是obj的编译时类型。]这些将是很好的候选人,以这种方式重写,采用obj的运行时类型。
如果我们这样做了,就会使方法体更难阅读:父类中的静态调用可能会被动态地“重新路由”。为了避免这种情况,我们必须使用类名调用静态方法——这使得调用更明显地用编译时类型层次结构来解析(就像现在一样)。
调用静态方法的其他方法更加棘手:this. staticmethod()的意思应该与obj.staticMethod()相同,接受this的运行时类型。然而,这可能会给现有程序带来一些麻烦,这些程序调用(显然是本地的)没有修饰的静态方法(可以说相当于this.method())。
那么无修饰调用staticMethod()会怎样呢?我建议他们像今天一样,使用本地类上下文来决定要做什么。否则就会产生巨大的混乱。当然,如果method是非静态方法,则method()意味着this.method(),如果method是静态方法,则意味着ThisClass.method()。这是另一个困惑的来源。
其他的考虑
如果我们改变了这种行为(并使静态调用具有潜在的动态非本地性),我们可能会希望重新审视final、private和protected作为类静态方法的限定符的意义。然后,我们都必须习惯这样一个事实:私有静态方法和公共final方法不会被覆盖,因此可以在编译时安全地解析,并且可以“安全”地作为本地引用读取。