为什么Java不允许覆盖静态方法?

582

为什么不能覆盖静态方法?

如果可以的话,请举一个例子。


3
大多数面向对象编程语言不允许这样做。 - jmucchiello
11
@jmucchiello:看我的回答。我一开始也和你想的一样,但后来了解了Ruby/Smalltalk的“类”方法,所以还有其他真正的面向对象编程语言可以做到这一点。 - Kevin Brock
6
大多数面向对象编程语言并非真正的面向对象编程语言(我想到了Smalltalk)。 - mathk
2
可能是因为Java在编译时解析静态方法的调用。因此,即使您编写了Parent p = new Child(),然后编写了p.childOverriddenStaticMethod(),编译器也会通过查看引用类型将其解析为Parent.childOverriddenStaticMethod() - Manoj
哇,我们现在可以编写像http://hastebin.com/codajahati.java这样的东西了。使用这种模式(最好是用不那么笨拙的代码),Java可以从核心库中消除几乎所有的异常(可能不会,也可能会),或者我们可以只是包装一下事情。 - Dmytro
显示剩余2条评论
22个回答

521

覆盖取决于类的实例。多态的重点在于您可以对类进行子类化,而实现这些子类的对象将为超类中定义的相同方法具有不同的行为(并在子类中覆盖)。静态方法与类的任何实例都没有关联,因此该概念不适用。

Java设计时考虑了两个方面对此产生了影响。一方面是性能方面的担忧:Smalltalk受到了太慢的批评(垃圾收集和多态调用是其中的一部分),而Java的创建者则决心避免这种情况。另一个考虑因素是Java的目标受众是C++开发人员。使静态方法按照现有方式工作的好处是对于C++程序员来说非常熟悉,并且非常快速,因为无需等待运行时来确定要调用哪个方法。


22
但仅在Java中是“正确的”。例如,Scala中等价于“静态类”(称为objects)的东西允许方法重载。 - user166390
35
Objective-C 也允许重写类方法。 - Richard
12
有编译时类型层级和运行时类型层级。在存在后者的情况下,询问为什么静态方法调用不利用运行时类型层级是完全有意义的。在Java中,当从对象(obj.staticMethod())调用静态方法时,这种情况就会发生——这是允许的并且使用编译时类型。当静态调用位于类的非静态方法中时,“当前”对象可能是该类的派生类型,但是派生类型上定义的静态方法不予考虑(它们在运行时类型层级中)。 - Steve Powell
19
我应该表明清楚:这个概念不适用 是 _不正确的_。 - Steve Powell
16
虽然这个回答是正确的,但更像是描述现状而非应该如何或更准确地说,为了满足问问题者和我们这里的期望,应该如何操作。目前并没有明确的理由禁止覆盖静态方法,除非是因为“就是这样”。我个人认为这是一个缺陷。 - RichieHH
显示剩余10条评论

199

我个人认为这是 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%的奖金。但是如果您删除"static",则SpecialEmployee将获得3%的奖金。

(诚然,这个例子的编码风格很差,因为在现实生活中,您可能希望奖金乘数在某个数据库中而不是硬编码。但这只是因为我不想用许多与重点无关的代码降低示例质量。)

我认为,使getBonusMultiplier静态化是很有道理的。也许您希望能够显示所有类别员工的奖金乘数,而无需在每个类别中都拥有员工实例。寻找此类示例实例的目的是什么?如果我们正在创建新的员工类别,并且尚未分配任何员工怎么办?这种情况下,它就变成了一个静态函数。

但它没有起作用。

是的,我可以想到许多重写上述代码以使其正常工作的方法。我的观点不是它会导致一个无法解决的问题,而是它会给不谨慎的程序员设置陷阱,因为该语言的行为并不像我认为合理的人所期望的那样。

也许,如果我尝试编写面向对象语言的编译器,我很快就会看到为什么实现静态函数可以被覆盖会很困难或不可能。

或者也许Java之所以表现出这种方式,有一些很好的原因。有人能指出这种行为的优点吗?是否有某些问题类别因此变得更容易了?我的意思是,不要只是指向Java语言规范并说"看,这是它的行为文档。" 我知道这个。但是有没有一个好的理由它应该表现出这种方式?(除了明显的"使它正常工作过于困难"...)

更新

@VicKirk:如果您的意思是这是“糟糕的设计”,因为它不符合Java处理静态的方式,我的回答是:“嗯,当然。”正如我在原始帖子中所说,它不起作用。但是如果您的意思是,在这种情况下存在某些根本性问题,即静态可以像虚函数一样被覆盖,这将引入歧义或无法有效实现等问题时,我会回答:“为什么?这个概念有什么问题吗?”

我认为,我给出的例子是一个非常自然的想法。我有一个类,它有一个函数,该函数不依赖于任何实例数据,并且我可能非常合理地希望在没有实例的情况下调用它,同时还希望从实例方法中调用它。为什么不能这样做?多年来,我遇到过这种情况。实际上,我通过使函数虚拟并创建一个唯一目的是成为静态方法的静态方法,然后使用虚拟方法传递呼叫及其虚拟方法,其中包含一个虚拟实例。这似乎是一个非常迂回的方法。


12
@Bemrose:但这正是我的观点:为什么我不能这样做呢?也许我的“静态”概念和你的不同,但基本上我认为“静态”是一种可以成为静态方法的方法,因为它不使用任何实例数据,并且应该是静态的,因为您可能希望独立于实例调用它。静态与类显然是相关的:我期望 Integer.valueOf 与 Integers 相关联,Double.valueOf 与 Doubles 相关联。 - Jay
10
@ewernli和Bemrose:对,对,事情就是这样。我不会质疑这一点。由于我的示例代码无法工作,当然不会尝试编写它。我的问题是为什么会这样。(我担心这将变成我们无法沟通的那种对话。"请问销售员先生,我能不能拿一个红色的?" "不行,要5美元。" "好的,我知道要5美元,但是我想要一个红色的。" "先生,我刚告诉过您它要5美元。" "好吧,我知道价格,但是我在问颜色。" "我已经告诉过您价格了!" 等等。) - Jay
7
我认为最终这段代码很令人困惑。考虑如果实例被作为参数传递。那么你的意思是运行时实例应该决定调用哪个静态方法。这基本上会创建一个完全独立于现有实例层次结构的平行层次结构。现在如果子类定义了与非静态方法相同的方法签名呢?我认为规则会让事情变得非常复杂。正是这些语言复杂性,Java试图避免。 - Yishai
7
@Yishai:RE “运行时实例决定调用哪个静态方法”:完全正确。我不明白为什么你不能像虚函数一样处理静态函数。“分离的继承结构”:我认为,应该将其纳入相同的继承结构中。为什么不能将静态函数包含在相同的继承结构中呢?“子类定义相同签名的非静态函数”:我猜这是非法的,就像覆盖具有相同签名但不同返回类型的函数,或不抛出父函数所抛出的所有异常,或具有更窄的作用域一样。 - Jay
31
我认为Jay说得对——当我发现静态方法无法被覆盖时,我也感到惊讶。这在某种程度上是因为,如果我有一个带有方法 someStatic() 的类A,并且B继承自A,则B.someMethod()绑定到A中的该方法。如果随后向B中添加someStatic()方法,则调用代码仍将调用A.someStatic(),直到重新编译调用代码。同时,令人惊讶的是,bInstance.someStatic()使用bInstance声明的类型而不是运行时类型,因为它在编译时而不是链接时进行绑定,所以如果存在B.someStatic(),则A bInstance; ... bInstance.someStatic()将调用A.someStatic()。 - Lawrence Dol
显示剩余28条评论

44

简短回答是:它完全可行,但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() 调用(通常会出现警告)。[注:ObjectClassobj 的编译时类型。] 这些将是以此方式覆盖的良好候选项,采用 obj 的运行时类型。

如果我们这样做,将使方法体更难阅读:父类中的静态调用可能会被“动态”重新路由。为避免这种情况,我们必须使用类名调用静态方法,从而使调用更明显地使用编译时类型层次结构进行解析(与现在相同)。

其他调用静态方法的方式则更加棘手:this.staticMethod() 应该与 obj.staticMethod() 意思相同,采用 this 的运行时类型。但是,这可能会对现有程序造成一些麻烦,这些程序调用(显然是本地的)静态方法而没有装饰(这可以说等效于 this.method())。

那么裸露的调用 staticMethod() 呢?我建议它们与今天做法相同,并使用本地类上下文来决定该怎么做。否则将产生极大的混乱。当然,这意味着如果 method 是非静态方法,则 method() 将意味着 this.method(),如果 method 是静


如果我们这样做,方法主体将会更难以阅读:父类中的静态调用可能会被动态“重新路由”。没错,但这正是普通非静态函数调用现在所发生的。这通常被标榜为普通虚函数的一个积极特征,而不是问题。 - Jay

26

事实上我们错了。
尽管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声明为RegularEmployee并将其分配为SpecialEmployee的实例,我们仍将在第一种情况下调用RegularEmployee方法,在第二种情况下调用SpecialEmployee方法。

RegularEmployee Carl = new SpecialEmployee();

System.out.println(Carl.calculateBonus());
System.out.println(Carl.calculateOverridenBonus());

只需查看输出控制台:

0.02
0.03

;)


11
是的,反思几乎是唯一能做的事情——但问题并不完全是这样——将它写在这里仍然很有用。 - Mr_and_Mrs_D
2
这个答案是我在所有Java主题中看到的最大的hack。不过读起来还是很有趣的 :) - Andrejs

19

静态方法在JVM中被视为全局方法,它们不与任何对象实例绑定。

如果你能像Smalltalk这样的语言一样从类对象中调用静态方法,那理论上是可能的,但在Java中不是这样的情况。

编辑

您可以重载静态方法,这是可以的。但是您不能覆盖静态方法,因为类不是一等对象。您可以使用反射在运行时获取对象的类,但您获取的对象与类层次结构不相对应。

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()。静态方法不绑定到对象,因此在静态方法中没有thissuper的概念。静态方法是全局函数;因此也没有多态的概念,因此方法覆盖没有意义。

但是如果MyClass在运行时是一个对象,你可以像Smalltalk那样调用它的方法(或者可能是JRuby,正如一条评论所提到的,但我对JRuby一无所知)。

哦对了...还有一件事。你可以通过对象obj1.staticMethod()调用静态方法,但这实际上是MyClass.staticMethod()的语法糖,应该避免使用。在现代IDE中,通常会引发警告。我不知道为什么他们曾经允许这种快捷方式。


5
许多现代编程语言,如Ruby,都有类方法并允许覆盖它们。 - Chandra Sekar
3
在Java中,类作为对象存在。可以查看"Class"类。我可以使用myObject.getClass()方法,它将返回适当类的实例对象。 - Jay
5
你只能获得这个课程的“描述” - 而不是课程本身。但是这种差别微妙。 - ewernli
你的类仍然存在,但它被隐藏在虚拟机中(靠近类加载器),用户几乎无法访问它。 - mathk
1
为了正确使用 clazz2 instanceof clazz1,您可以改用 class2.isAssignableFrom(clazz1),我相信这在您的示例中会返回 true。 - Simon Forsberg

17

方法重写是通过动态分派实现的,这意味着对象的声明类型并不决定其行为,而是由其运行时类型决定:

Animal lassie = new Dog();
lassie.speak(); // outputs "woof!"
Animal kermit = new Frog();
kermit.speak(); // outputs "ribbit!"

尽管lassiekermit都被声明为Animal类型的对象,但它们的行为(方法.speak())不同,因为动态分派仅会在运行时bind方法调用.speak()到一个实现 - 而不是在编译时。

现在,这里就是static关键字开始有意义的地方:单词“static”是“dynamic”的反义词。因此,您无法覆盖静态方法的原因是因为静态成员上没有动态分派 - 因为静态字面上意味着“不动态”。如果它们是动态分派的(因此可以被覆盖),那么static关键字就不再有意义了。


我认为,人们不应该通过其内部实现来争论“静态”与“动态”。那是语言的API关键字。程序员期望什么?静态方法在运行时返回恒定结果(针对特定类)=> 静态。成员函数返回结果,取决于实例变量(可以在运行时更改)=> 动态。 - kaiser

14
是的。在实际中Java允许重写静态方法,但从理论上讲,如果您在Java中覆盖了静态方法,则它将编译和运行顺畅,但会失去Java的基本属性——多态性。您将在各处阅读到无法尝试自己编译和运行的信息。例如,如果您有一个类Animal和一个静态方法eat(),并且您在它的子类中覆盖了该静态方法,称为Dog。然后,当您将Dog对象分配给Animal引用并调用eat()时,按照Java Dog的eat()应该被调用,但在静态覆盖中将调用Animals' eat()。
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

根据Java中的多态原则,输出应该是“Dog Eating”。但结果不同,因为为了支持多态性,Java使用了后期绑定,这意味着方法仅在运行时调用,而不是静态方法的情况。在静态方法中,编译器在编译时调用方法,而不是在运行时调用,因此我们根据引用而不是对象来获取方法,这就是为什么你可以说它实际上支持静态重写,但从理论上讲,它并不支持。

6
从对象中调用静态方法是不好的做法。 - Dmitry Zagorulkin

7
在Java(以及许多OOP语言中,但我不能代表全部;有些根本没有静态)中,所有方法都有一个固定的签名 - 参数和类型。在虚拟方法中,第一个参数被隐含:对对象本身的引用,当从对象内部调用时,编译器会自动添加this
对于静态方法来说没有区别 - 它们仍然有一个固定的签名。但是,通过声明该方法为静态,您已经明确表示编译器不必在该签名开头包含隐含的对象参数。因此,任何其他调用此方法的代码都不能尝试将对象引用放入堆栈上。如果这样做了,那么方法执行将无法工作,因为参数将在堆栈上错位 - 向前移动一个位置。
由于这两者之间的差异;虚拟方法始终具有对上下文对象(即this)的引用,因此可以引用属于该对象实例的堆中的任何内容。但是对于静态方法,由于未传递引用,该方法无法访问任何对象变量和方法,因为上下文是未知的。
如果您希望Java更改定义,以便为每个方法传递对象上下文,则实质上只会有虚拟方法。
正如评论中的某人所问 - 您希望此功能的原因和目的是什么?
我不太了解Ruby,因为这是由OP提到的,我做了一些研究。我看到在Ruby中,类实际上是一种特殊类型的对象,可以创建(甚至是动态的)新方法。在Ruby中,类是完整的类对象,而在Java中则不是。这仅仅是您在使用Java(或C#)时必须接受的事实。这些不是动态语言,尽管C#正在添加某些形式的动态。实际上,据我所知,Ruby没有“静态”方法 - 在这种情况下,这些是单例类对象上的方法。然后,您可以使用新类覆盖此单例类,并且以前类对象中的方法将调用在新类中定义的方法(对吗?)。因此,如果在原始类的上下文中调用了一个方法,则仍然只会执行原始静态方法,但是调用派生类中的方法将调用父级或子类中的方法。有趣的是,我可以看到其中一些价值。它需要一种不同的思考方式。
由于您正在使用Java,因此您需要适应那种做事的方式。他们为什么这样做呢?嗯,可能是基于当时可用的技术和理解来提高性能。计算机语言不断发展。回到足够远的过去,根本没有面向对象编程。在未来,将出现其他新想法。 编辑:还有一个评论。现在我看到了这些差异,并且作为一名Java/C#开发人员,我可以理解如果您来自像Ruby这样的语言,那么从Java开发人员那里得到的答案可能会令人困惑。 Java的static方法与Ruby的class方法不同。对于Java开发人员来说,理解这一点将很困难,反之亦然,对于大多使用Ruby/Smalltalk等语言的人也是如此。我可以看出,这也会极大地混淆事实,即Java还使用“类方法”作为另一种谈论静态方法的方式,但是Ruby会以不同的方式使用相同的术语。Java没有Ruby风格的类方法(很抱歉); Ruby没有Java风格的静态方法,这实际上只是旧的过程式风格函数,就像C中找到的那样。

顺便说一下-感谢您的问题!今天我学到了关于类方法(Ruby风格)的新知识。


1
当然,Java没有理由不能将“Class”对象作为静态方法的隐藏参数传递。只是它没有被设计成这样。 - jmucchiello

6
如果从Java中重写方法应该如何行为的角度考虑,答案是否定的。但是,如果您尝试覆盖静态方法,则不会收到任何编译器错误。这意味着,如果您尝试覆盖,Java不会阻止您这样做;但是,您肯定无法获得与非静态方法相同的效果。在Java中重写只是意味着特定的方法将基于对象的运行时类型而不是编译时类型进行调用(这是重写静态方法的情况)。为什么它们表现奇怪?因为它们是类方法,因此始终仅使用编译时类型信息在编译时解析对它们的访问。使用对象引用访问它们只是Java设计者赋予我们的额外自由,当然我们一定不要认为只有当他们限制它时才停止这种做法。
例如:让我们尝试查看覆盖静态方法会发生什么:
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: inside staticMethod
SuperClass: inside staticMethod
SubClass: inside staticMethod

请注意输出的第二行。如果staticMethod已被覆盖,这一行应与第三行相同,因为我们正在使用“SubClass”作为运行时类型的对象调用“staticMethod()”,而不是“SuperClass”。这证实了静态方法始终仅使用其编译时类型信息进行解析。


6

我喜欢并赞同Jay的评论(https://dev59.com/r3E95IYBdhLWcg3wn_f2#2223803)。

我认为这是Java的糟糕设计。许多其他编程语言都支持重写静态方法,正如我们在之前的评论中所看到的。

我感觉Jay和我一样,也是从Delphi转向了Java。

Delphi(Object Pascal)是在Java之前实现面向对象编程的语言之一,也是最早用于商业应用程序开发的语言之一。

很明显,许多人都有使用过这种语言的经验,因为过去它是唯一用于编写商业GUI产品的语言。是的,在Delphi中,我们可以重写静态方法。实际上,Delphi中的静态方法称为“类方法”,而Delphi具有不同的“Delphi静态方法”概念,这些方法采用早期绑定方式。要重写方法,您必须使用后期绑定,并声明“虚拟”指令。因此,这非常方便和直观,我希望Java也能实现这一点。


Delphi(Object Pascal)是第一种支持OOP的编程语言。Simula、SmallTalk和其他一些支持OOP的编程语言都在Delphi之前出现。 - WJS
@WJS我从未听说过Simula或其他支持OOP的语言。我认为Delphi是第一种实现OOP并被广泛应用于商业应用开发的语言。看来我需要重新表述这个陈述。 - Patlatus

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接