为什么Java不允许重写静态方法?
个人认为这是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%的奖金。但是如果您删除“static”,那么特殊员工将获得3%的奖金。
(不可否认,此示例是编码风格不好,因为在现实生活中,您可能希望奖金乘数保存在某个数据库中,而不是硬编码。但这只是因为我不想用许多与重点无关的代码拖延例子。)
我认为您可能希望将getBonusMultiplier设置为static。也许您想要显示所有员工类别的奖金乘数,而无需在每个类别中均有一个员工实例。寻找这样的实例有什么意义呢?如果我们正在创建一个尚未分配员工的新员工类别怎么办?这是一个非常逻辑上的静态功能。
但它行不通。
是的,是的,我可以想到许多重写上述代码以使其正常运行的方法。我的重点不是它会造成无法解决的问题,而是它会为粗心的程序员制造陷阱,因为语言的行为并不像我认为的一个合理的人所预期的那样。
也许,如果我尝试编写面向对象编程语言的编译器,我会很快看到为什么实现静态函数可以被覆盖会很困难或不可能。
或许Java之所以这样行事是有一些好的原因的。有人能指出这种行为的某些优点吗?是否有某些问题类别因此变得更容易?我的意思是,不要仅仅指向Java语言规范并说“看,这是它的行为记录”。我知道了。但是,有没有一个好的理由让它应该这样行事?(除了明显的“让它正确地工作太难了”之外……)
更新
@VicKirk:如果你的意思是说这是“糟糕的设计”,因为它不符合Java处理静态成员的方式,我回答,“嗯,当然。”正如我在我的原始文章中所说的,它行不通。但是,如果你的意思是说,这是坏设计的意义在于:在一种静态成员可以像虚函数一样被覆盖的语言中将会有某些根本性的问题,例如,这将会引入不确定性,或者无法高效地实现等等,我会问,“为什么?这个概念有什么问题吗?”
我认为我给出的例子是非常自然的一件事情。我有一个类,它有一个函数,不依赖于任何实例数据,我可能非常合理地希望独立于实例调用它,并且想从实例方法中调用它。为什么这不能行事?多年来,我遇到了这种情况不少次。实际上,我通过使函数变为虚函数来解决它,并创建一个唯一目的就是传递带有虚拟实例的调用的静态方法。这似乎是一个非常绕弯的方式来解决它。