为什么Java不允许重写静态方法?
为什么不能重写静态方法?
如果可能,请使用示例。
为什么不能重写静态方法?
如果可能,请使用示例。
重写取决于具有类的实例。多态性的要点是,您可以对类进行子类化,并且实现这些子类的对象对于在超类中定义的相同方法将具有不同的行为(并在子类中重写)。静态方法不与类的任何实例相关联,因此该概念不适用。
驱动Java设计的两个因素影响了这一点。一个是对性能的担忧:有很多人批评Smalltalk太慢(垃圾收集和多态调用是其中的一部分),Java的创建者决心避免这种情况。另一个是决定Java的目标受众是C++开发人员。让静态方法以这种方式工作,对C++程序员来说具有熟悉度的好处,而且速度也非常快,因为没有必要等到运行时才能弄清楚要调用哪个方法。
我个人认为这是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");
}
}
此代码将无法按预期工作。也就是说,SpecialEmployee可以像普通员工一样获得2%的奖金。但是,如果您删除“静态”,那么SpecialEmployee将获得3%的奖金。
(诚然,这个例子的编码风格很差,因为在现实生活中,你可能希望奖金乘数在某个地方的数据库中,而不是硬编码的。但这只是因为我不想用大量与这一点无关的代码来使示例陷入困境。
在我看来,您可能希望使getBonusMultiplier保持静态,这似乎是相当合理的。也许您希望能够显示所有类别员工的奖金乘数,而无需在每个类别中都有一个员工的实例。搜索此类示例实例的意义何在?如果我们正在创建一个新的员工类别,但尚未分配任何员工,该怎么办?这在逻辑上是一个静态函数。
但它不起作用。
是的,是的,我可以想出任何方法重写上面的代码以使其正常工作。我的观点不是它创造了一个无法解决的问题,而是它为粗心的程序员创造了一个陷阱,因为语言的行为并不像我认为一个理性的人所期望的那样。
也许如果我试图为OOP语言编写编译器,我很快就会明白为什么实现它以便可以覆盖静态函数将是困难的或不可能的。
或者也许Java有一些很好的理由来解释为什么Java会这样表现。任何人都可以指出这种行为的一个优势,一些类别的问题,通过这种行为变得更容易?我的意思是,不要只是指给我Java语言规范,然后说“看,这是记录它的行为方式”。我知道那件事。但是,为什么它应该以这种方式表现,是否有充分的理由呢?(除了明显的“让它正常工作太难了”...)
更新
@VicKirk:如果你的意思是这是“糟糕的设计”,因为它不符合Java处理静态的方式,我的回答是,“嗯,嗯,当然。正如我在原始帖子中所说,它不起作用。但是,如果你的意思是说这是糟糕的设计,从某种意义上说,一种语言在工作的地方会有一些根本性的错误,即静态可以像虚函数一样被覆盖,这会以某种方式引入歧义,或者不可能有效地实现或类似的东西,我回答说,“为什么?这个概念有什么问题?
我认为我给出的例子是一件非常自然的事情。我有一个类,它有一个不依赖于任何实例数据的函数,并且我可能非常合理地想要独立于实例调用它,并且想要从实例方法中调用。为什么这不起作用?多年来,我遇到过这种情况很多次。在实践中,我通过使函数成为虚拟的,然后创建一个静态方法来解决它,该方法在生活中的唯一目的是成为一个静态方法,该方法将调用传递给具有虚拟实例的虚拟方法。这似乎是一种非常迂回的方式。