为什么不可能重写静态方法?

如果可能,请举例说明。


当前回答

下面的代码表明这是可能的:

class OverridenStaticMeth {   

static void printValue() {   
System.out.println("Overriden Meth");   
}   

}   

public class OverrideStaticMeth extends OverridenStaticMeth {   

static void printValue() {   
System.out.println("Overriding Meth");   
}   

public static void main(String[] args) {   
OverridenStaticMeth osm = new OverrideStaticMeth();   
osm.printValue();   

System.out.println("now, from main");
printValue();

}   

} 

其他回答

通过重写,我们可以根据对象类型创建一个多态性质。静态方法与对象无关。因此java不支持静态方法重写。

Well... the answer is NO if you think from the perspective of how an overriden method should behave in Java. But, you don't get any compiler error if you try to override a static method. That means, if you try to override, Java doesn't stop you doing that; but you certainly don't get the same effect as you get for non-static methods. Overriding in Java simply means that the particular method would be called based on the run time type of the object and not on the compile time type of it (which is the case with overriden static methods). Okay... any guesses for the reason why do they behave strangely? Because they are class methods and hence access to them is always resolved during compile time only using the compile time type information. Accessing them using object references is just an extra liberty given by the designers of Java and we should certainly not think of stopping that practice only when they restrict it :-)

示例:让我们试着看看如果我们尝试重写一个静态方法会发生什么:-

class SuperClass {
// ......
public static void staticMethod() {
    System.out.println("SuperClass: inside staticMethod");
}
// ......
}

public class SubClass extends SuperClass {
// ......
// overriding the static method
public static void staticMethod() {
    System.out.println("SubClass: inside staticMethod");
}

// ......
public static void main(String[] args) {
    // ......
    SuperClass superClassWithSuperCons = new SuperClass();
    SuperClass superClassWithSubCons = new SubClass();
    SubClass subClassWithSubCons = new SubClass();

    superClassWithSuperCons.staticMethod();
    superClassWithSubCons.staticMethod();
    subClassWithSubCons.staticMethod();
    // ...
}
}

输出: SuperClass:在staticMethod内部 SuperClass:在staticMethod内部 子类:staticMethod内部

注意输出的第二行。如果staticMethod被重写,这一行应该与第三行相同,因为我们在运行时类型的对象上调用'staticMethod()'作为'子类'而不是'超类'。这证实了静态方法总是只使用它们的编译时类型信息进行解析。

我个人认为这是Java设计中的一个缺陷。是的,是的,我理解非静态方法附加到实例,而静态方法附加到类,等等。不过,考虑下面的代码:

public class RegularEmployee {
    private BigDecimal salary;

    public void setSalary(BigDecimal salary) {
        this.salary = salary;
    }

    public static BigDecimal getBonusMultiplier() {
        return new BigDecimal(".02");
    }

    public BigDecimal calculateBonus() {
        return salary.multiply(getBonusMultiplier());
    }

    /* ... presumably lots of other code ... */
}

public class SpecialEmployee extends RegularEmployee {
    public static BigDecimal getBonusMultiplier() {
        return new BigDecimal(".03");
    }
}

这段代码不能像您期望的那样工作。也就是说,特殊员工和普通员工一样有2%的奖金。但如果你去掉了“静态”,那么SpecialEmployee就能得到3%的奖金。

(不可否认,这个例子的编码风格很差,因为在现实生活中,你可能希望奖励乘数在数据库的某个地方,而不是硬编码。但这只是因为我不想用大量与主题无关的代码使示例陷入困境。)

在我看来,您可能想要使getBonusMultiplier成为静态的,这似乎很合理。也许您希望能够显示所有员工类别的奖金乘数,而不需要在每个类别中有一个员工实例。搜索这样的例子有什么意义呢?如果我们正在创建一个新的员工类别,并且还没有任何员工分配给它,该怎么办?这在逻辑上是一个静态函数。

但这并不奏效。

是的,是的,我可以想出很多方法来重写上面的代码,让它工作。我的观点不是它产生了一个无法解决的问题,而是它为粗心的程序员制造了一个陷阱,因为这种语言的行为不像我认为一个理性的人所期望的那样。

也许如果我试着为OOP语言编写一个编译器,我很快就会明白为什么实现它以覆盖静态函数是困难的或不可能的。

或许有一些很好的理由来解释为什么Java会这样做。有人能指出这种行为的好处吗,这种行为能让一些问题变得更简单吗?我的意思是,不要只是把我指给Java语言规范,然后说“看,这是它如何行为的文档”。我知道。但是,它为什么会有这样的表现,有一个很好的理由吗?(除了明显的“让它正常工作太难了”……)

更新

@VicKirk: If you mean that this is "bad design" because it doesn't fit how Java handles statics, my reply is, "Well, duh, of course." As I said in my original post, it doesn't work. But if you mean that it is bad design in the sense that there would be something fundamentally wrong with a language where this worked, i.e. where statics could be overridden just like virtual functions, that this would somehow introduce an ambiguity or it would be impossible to implement efficiently or some such, I reply, "Why? What's wrong with the concept?"

I think the example I give is a very natural thing to want to do. I have a class that has a function that does not depend on any instance data, and which I might very reasonably want to call independent of an instance, as well as wanting to call from within an instance method. Why should this not work? I've run into this situation a fair number of times over the years. In practice I get around it by making the function virtual, and then creating a static method whose only purpose in life is to be a static method that passes the call on to the virtual method with a dummy instance. That seems like a very roundabout way to get there.

简短的回答是:这是完全可能的,但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!";
  }

}