为什么在Java中静态方法不能是抽象的?

11 浏览
0 Comments

为什么在Java中静态方法不能是抽象的?

为什么在Java中不能定义抽象静态方法?例如:\n

abstract class foo {
    abstract void bar( ); // <-- 这是可以的
    abstract static void bar2(); //<-- 为什么不可以?
}

0
0 Comments

为什么Java中的静态方法不能是抽象的?

在Java中,不能重写静态方法,因此使其抽象是没有意义的。此外,抽象类中的静态方法将属于该类,而不是继承类,因此无法使用。如果你想要基于实例的行为,可以使用实例方法。

然而,这个回答是不正确的。抽象类中的静态方法可以正常工作并且经常被使用。只是类本身的静态方法不能是抽象的。因为没有实例,运行时如何知道要调用哪个方法呢?

即使没有实例,类层次结构仍然存在,静态方法的继承可以像实例方法的继承一样工作。Smalltalk就是这样做的,而且非常有用。

好消息是,根据Java 7的官方文档(参见docs.oracle.com/javase/tutorial/java/IandI/abstract.html),"抽象类可以有静态字段和静态方法。你可以像使用任何其他类一样使用这些静态成员 - 例如,AbstractClass.staticMethod()。"

但这并不是什么新鲜事。抽象类一直允许具有静态的非抽象方法。

所以,不幸的是,Java 7中并没有改变这一点。

这正是为什么我们不能拥有好东西的众多原因之一。

0
0 Comments

为什么Java中的静态方法不能是抽象的?

Java中的静态方法不能是抽象的,这是由于编程语言设计上的限制。这种限制的原因可能是为了保护面向对象编程的原则,限制程序员由于不必要的语言特性而违反面向对象的原则。

Java中的静态方法是直接通过类名调用的,而不需要创建实例对象。如果静态方法可以是抽象的,那么就可以直接调用抽象方法,而不需要创建实例对象。这样做会更加高效。特别是当使用抽象类作为枚举的替代方案时,这一限制就变得更为明显。这也是Java设计的不足之一。希望在未来的版本中能够解决这些限制。

Java语言中存在很多奇怪的限制。例如,闭包只能访问final变量,这也是一个限制。还有很多其他限制,几乎是无穷无尽的。作为Java程序员,了解这些限制及其解决方法是我们的工作。为了有趣,我们在业余时间会使用比Java更好的语言。但是我不会过于担心这些限制,因为这是取得进步的动力。

有人认为所谓的“Poor language design”更多的是一种“Protective language design”,其目的是限制程序员由于不必要的语言特性而违反面向对象编程的原则。

“抽象静态”概念是否违反了面向对象的原则?实际上并没有,但是当然也有人认为,static本身的概念已经是一种违反。

静态的需求清楚地表明了“面向对象原则”并不像通常声称的那样全面。静态方法并不是绝对必需的,许多面向对象的纯粹主义者会告诉你静态方法是一种憎恶。

C++中也存在同样的限制。

总之,Java中静态方法不能是抽象的是由于编程语言设计上的限制。这种限制可能是为了保护面向对象编程的原则,限制程序员由于不必要的语言特性而违反面向对象的原则。希望在未来的版本中能够解决这些限制。

0
0 Comments

为什么Java中的静态方法不能是抽象的?

在Java中,"abstract"表示"没有实际功能",而"static"表示"即使没有对象实例也有功能"。这是一个逻辑矛盾。

更简洁的答案是"糟糕的语言设计"。静态应该表示"属于类",因为这是直观上的使用方式,正如这个问题本身所展示的。在Python中有"classmethod"。

Java中的静态就是"属于类"的意思。我道歉,我的表述不够清楚。当然,静态方法是"属于类"的,它只是存在于相同的命名空间中。静态方法不是类对象本身的方法:它不使用类对象作为"this",也不能正确地参与继承链。如果它真的是一个类方法,那么"abstract static"就是完全合理的。它将成为类对象本身的方法,子类对象必须实现它。当然,尽管我对语言进行了抱怨,但根据目前的情况,你的答案是正确的。

这不是一个逻辑矛盾,而是语言的不足。其他许多语言支持这个概念。"abstract"表示"在子类中实现","static"表示"在类上执行而不是类实例"。并没有逻辑矛盾。

仍然,你所说的并不适用于"abstract static":一个在子类中实现的函数X不能同时在类上执行,只能在子类上执行。在子类上执行时,它不再是抽象的。

我同意。要成为一个逻辑矛盾,逻辑必须起源于Java规则之外。在Java中,"static"并不意味着"不为空" - 这只是Java不允许静态方法是抽象方法的结果。它的意思是"在类上可调用"(虽然它应该意味着"只能在类上可调用",但这是另一个问题)。如果Java支持"abstract static"方法,我期望它意味着这个方法必须由子类实现,并且它是子类的类方法。有些方法作为实例方法是没有意义的。不幸的是,在创建抽象基类(或接口)时,Java不允许您指定这一点。

我目前遇到的一个明显的问题是,当您在继承的构造函数中需要知道子对象的类时(深层次的异常层次结构):由于它是属于类的信息,您必须使此方法为静态方法,但是由于您无法继承静态方法,您需要以某种方式使其成为抽象的;您可以使用非静态方法,但问题是非静态方法在构造函数中无法调用;所以在继承的构造函数中,您就无法知道您实例化的对象的类型,除非您将其作为参数传递!这简直太愚蠢了。

我同意Alexander的观点,但是一种方法是定义一个静态方法,并调用相同类的单例实例的实现。然后,可以通过将单例替换为另一个子类实现来重写该方法。

对于Java中的许多事情来说,从另一种语言过来,它似乎是一种限制,但后来你会意识到这实际上是你不应该做的事情。例如,在Leo的评论中,如果您需要在继承的构造函数中知道子类,那是对基本面向对象原则的明显违反。另外,你打算如何调用这样的方法?要调用静态方法,您需要类名。使用基类名称,您怎么知道您想要的是哪个子类?如果您知道子类,它本来就不需要是抽象的。请展示一下这将如何工作的语法。

如果有一种方法可以调用它,那么您如何知道抽象方法是否被实现了?一个类在实例化之前必须实现所有的抽象方法,但对于静态方法,您不实例化类。那么,您如何首先知道它是否是有效的调用?例如,在Delphi中,您可以创建静态抽象方法,但是当您调用这样的方法时,也可能在运行时抛出异常。我总是更喜欢编译时错误而不是运行时错误。正如我所说的,从另一种语言来看,它似乎是有限制的,但Java试图通过良好的设计帮助您。

那么为什么在文档中说"抽象类可以有静态字段和静态方法",但在这里却不能有呢?

诸如抽象静态方法之类的功能往往会导致设计不良。抽象方法的整个目的是允许诸如方法工厂、模板方法等模式。当正确使用时,这些都是良好的设计模式,并且可以通过OO语言的多态性特性实现。另一方面,抽象静态方法除了强制子类实现某些东西之外,没有其他作用,这明显违反了几个OO设计原则(例如OCP)。

对我来说,这似乎是一个糟糕的"特性"。我仍然可以对一个类进行子类化,并希望覆盖该类的静态方法。这种方式迫使我实例化一个类,以便重写本来可以声明为静态的方法,前提是这些方法不会改变或使用实例状态。

-1,需要更多的反对意见。"static"并不意味着"即使没有对象实例也有功能"。"static"意味着"即使没有对象实例也可能有功能"。在既是"static"又是"abstract"的情况下,功能不会存在于父类中,而是在其子类中实现。

和Java中的许多事情一样,从Java来看,它似乎是正确的,但后来你会意识到它只是一种任意的语言限制。抽象静态方法可以存在于Java这样的强类型语言中,它不会导致运行时异常。

考虑一个静态抽象方法`T1 Parent.F1(T2 x)`,当我们调用它的子类实现`T1 Child.F1(T2 x)`时,不会出现运行时异常,所以为什么你说我们会有运行时异常呢?此外,抽象方法的整个目的是您不需要知道它是否实现。子类将实现它,并确保Liskov契约`T1 Parent.F1(T2 x)`得到保持。如果没有子类,那就没有问题,因为抽象方法只能在子类上调用。

我曾经有一个类`A`,它有一个`static main(String[] args)`方法,还有一个类`B`,它也有一个`static main(String[] args) throws Exception`方法。这导致了一个编译错误:无法覆盖方法。

仔细思考一下。你如何调用它?有三种方法:(1)直接使用子类名称,如Child.Method - 但在这种情况下,它不需要是抽象的,因为你正在引用一个特定的子类名称;(2)通过对象 - 但那时它不需要是静态的;(3)通过反射的Class实例 - 但是编译器无法保证Class实例是否定义了该方法,现在你将会在调用抽象方法时遇到运行时异常。底线是:需要静态抽象表示设计上的缺陷。

你几乎已经到达了目标。再走几步,你就会看到光明。我将指导你:你的第二个选项是正确的。它将通过对象实例调用,例如`obj_child.StaticF()`。因此,你的在Java的经验使你认为"但那时它不需要是静态的",我们的回答是"但那时它不需要是非静态的"。静态是方法的默认选项。只有引用对象实例的方法才应声明为非静态的。例如,如果我们有一个方法`f(){ return "hello world"; }`,它应该是静态的,因为没有引用对象实例。但是,如果我们有一个方法`f(){ return "hello world " + this.Name; }`,那么它应该是非静态的,因为引用了一个对象实例。静态应该是默认选项。在Java中,我们被迫将静态方法声明为非静态,因为Java中的静态继承是有问题的。正如Eric所说的,这是一种语言的不足。

从理论上讲,为什么静态是"默认"的?这听起来像是你刚刚捏造出来的。在过程式编程中也许是这样,在面向对象编程中实例方法才是默认的。不管怎样,你为什么如此关心它是静态的,如果你正在调用一个实例?这怎么会有任何实际的影响?

也许我应该将解释分成几个步骤。考虑这个方法`f(){ return "hello world"; }`。你有两个选择。1)将它设为静态。2)将它设为非静态。你会选择哪个,你认为哪个是默认的?

将其设置为父类中的静态方法会使其更加灵活,因为这意味着子类将永远不允许使用其实例字段。非静态是一种更可扩展、可重用的设计。只有在需要方便地在没有实例的情况下调用它时,才会将其设置为静态。如果我只计划在实例中调用该方法,如你所提到的场景,我会将其设置为非静态的。实例方法更具灵活性,即使语言支持静态方法的多态性,也不会对子类产生不必要的约束。

这不是不必要的约束,而是一种必要的约束,如果你关心代码和设计的美。将一个本质上是静态的函数(如`f(){ return "hello world"; }`)声明为非静态是一个丑陋的hack,因为这样做会将其不必要地与`this`变量耦合在一起。你所引用的"灵活性"是低耦合的一种违规。这和将所有字段和方法声明为`public`所获得的"灵活性"一样糟糕。

不管怎样,如果我想在抽象类中声明一个类方法,并希望子类实现,怎么办呢?

0