为什么不可能重写静态方法?
如果可能,请举例说明。
为什么不可能重写静态方法?
如果可能,请举例说明。
当前回答
简短的回答是:这是完全可能的,但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方法不会被覆盖,因此可以在编译时安全地解析,并且可以“安全”地作为本地引用读取。
其他回答
这个问题的答案很简单,标记为静态的方法或变量只属于类,因此静态方法不能在子类中继承,因为它们只属于超类。
简单的解决方案:使用单例实例。它将允许重写和继承。
在我的系统中,我有SingletonsRegistry类,它为传递的class返回实例。如果没有找到instance,则创建它。
Haxe语言类:
package rflib.common.utils;
import haxe.ds.ObjectMap;
class SingletonsRegistry
{
public static var instances:Map<Class<Dynamic>, Dynamic>;
static function __init__()
{
StaticsInitializer.addCallback(SingletonsRegistry, function()
{
instances = null;
});
}
public static function getInstance(cls:Class<Dynamic>, ?args:Array<Dynamic>)
{
if (instances == null) {
instances = untyped new ObjectMap<Dynamic, Dynamic>();
}
if (!instances.exists(cls))
{
if (args == null) args = [];
instances.set(cls, Type.createInstance(cls, args));
}
return instances.get(cls);
}
public static function validate(inst:Dynamic, cls:Class<Dynamic>)
{
if (instances == null) return;
var inst2 = instances[cls];
if (inst2 != null && inst != inst2) throw "Can\'t create multiple instances of " + Type.getClassName(cls) + " - it's singleton!";
}
}
重写静态方法有什么好处呢?不能通过实例调用静态方法。
MyClass.static1()
MySubClass.static1() // If you overrode, you have to call it through MySubClass anyway.
编辑:似乎由于语言设计中的一个不幸疏忽,您可以通过实例调用静态方法。一般没人会这么做。我的坏。
重写是为实例成员保留的,以支持多态行为。静态类成员不属于特定实例。相反,静态成员属于类,因此不支持重写,因为子类只继承受保护和公共实例成员,而不继承静态成员。您可能希望定义一个接口,并研究工厂和/或策略设计模式,以评估替代方法。
静态方法被JVM视为全局方法,根本不绑定到对象实例。
如果可以从类对象中调用静态方法(就像在Smalltalk等语言中那样),那么在概念上是可能的,但在Java中却不是这样。
EDIT
你可以重载静态方法,没关系。但是你不能重写静态方法,因为类不是一级对象。您可以使用反射在运行时获取对象的类,但所获得的对象并不与类层次结构并行。
class MyClass { ... }
class MySubClass extends MyClass { ... }
MyClass obj1 = new MyClass();
MySubClass obj2 = new MySubClass();
ob2 instanceof MyClass --> true
Class clazz1 = obj1.getClass();
Class clazz2 = obj2.getClass();
clazz2 instanceof clazz1 --> false
你可以对类进行反射,但它仅限于此。使用clazz1.staticMethod()不会调用静态方法,而是使用MyClass.staticMethod()。静态方法不绑定到对象,因此在静态方法中没有this或super的概念。静态方法是一个全局函数;因此,也没有多态性的概念,因此,方法重写没有意义。
但是,如果MyClass在运行时是一个调用方法的对象,这是可能的,就像在Smalltalk(或者可能是一个评论建议的JRuby,但我对JRuby一无所知)。
哦是的…还有一件事。您可以通过对象obj1.staticMethod()调用静态方法,但这实际上是MyClass.staticMethod()的语法糖,应该避免。在现代IDE中,它通常会引发一个警告。我不知道他们为什么允许走这条捷径。