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

hha*_*fez 572 java abstract-class static-methods

问题是在Java中为什么我不能定义一个抽象的静态方法?例如

abstract class foo {
    abstract void bar( ); // <-- this is ok
    abstract static void bar2(); //<-- this isn't why?
}
Run Code Online (Sandbox Code Playgroud)

Tom*_*lak 544

因为"abstract"意味着:"实现没有功能","静态"意味着:"即使你没有对象实例也有功能".这是一个合乎逻辑的矛盾.

  • 这不是一个逻辑上的矛盾,它是一种语言缺点,其他多种语言都支持这一概念."abstract"表示"在子类中实现","static"表示"在类而不是类实例上执行"没有逻辑矛盾. (673认同)
  • 一个更简洁的答案是"糟糕的语言设计".静态应该意味着"属于类",因为这就是它如何直观地使用,正如这个问题所表明的那样.请参阅Python中的"classmethod". (339认同)
  • @Tomakak:你的逻辑是循环的.`static`并不意味着"不为空" - 这只是Java不允许静态方法抽象的结果.这意味着"可以在课堂上调用".(它应该意味着"只能在类上调用*",但这是另一个问题.)如果Java支持`abstract static`方法,我希望它意味着方法1)必须由子类实现,并且2)是一个子类的类方法.有些方法作为实例方法没有意义.遗憾的是,Java不允许您在创建抽象基类(或接口)时指定它. (71认同)
  • @Tomalak我道歉,我不清楚.当然,静态方法'属于类'.尽管如此,它只是存在于同一名称空间中.静态方法不是类对象本身的方法:它不使用'this'作为类对象,并且它不能正确地参与继承链.如果真的是一个类方法`abstract static`就会很有意义.它是类对象本身的一种方法,子类对象必须实现它.当然,尽管我紧紧抓住语言,但事情的答案是正确的. (12认同)
  • @Eric:和*仍然*,你说的不适用于`abstract static`:一个"在子类中实现"的函数X*不能*同时"在类上执行" - 只在*上子类*.那时它不再是抽象的了. (10认同)
  • 与Java中的许多东西一样,来自另一种语言似乎是一种限制,但后来你意识到这是你不应该做的事情.例如,在Leo的注释中,如果你需要知道继承的构造函数中的子类,那就是公然违反基本的OOP原则.另外,您如何提出这样的方法?要调用静态方法,您需要类名.使用基类名称,您如何知道您想要的子类?然而,如果您知道子类,那么开始时它不需要是抽象的.显示语法如何工作. (8认同)
  • 我同意@Eric.要成为一个逻辑矛盾,逻辑需要源于java规则之外.在java中,"static"可能是"功能,即使没有对象实例",但除了语言设计之外,这并不与"abstract"相矛盾.`int`表示'一个整数'而`[]`表示'一个数组',但Java知道我想从`int []`得到的东西并不意味着它自相矛盾,而是java的人类编写者决定允许某种功能与特定的语法排列相关联. (7认同)
  • 如果有一种方法可以调用它,你怎么知道抽象方法是否已实现?类必须在实例化之前实现所有抽象方法,但对于静态方法,不要实例化类.那么你怎么知道它是否是一个有效的电话?例如,在Delphi中,您可以创建静态抽象方法,但在调用此类方法时也可以在运行时获取异常.我总是喜欢编译时错误到运行时错误.就像我说的那样,似乎限制来自另一种语言,但Java正试图帮助你进行良好的设计. (6认同)
  • @Pacerier想一想.*你怎么称呼它?三种方式:(1)直接使用子类名称,作为Child.Method - 但在这种情况下,它不需要是抽象的,因为你无论如何都指的是一个特定的子类名称; (2)通过一个物体 - 但它不需要是静止的; (3)通过带有反射的Class <?>实例 - 但是编译器不能保证Class实例具有定义的方法,如果在反映Parent时调用抽象方法,则现在遇到运行时异常.底线:需要静态抽象表示设计缺陷 (5认同)
  • 诸如抽象静态方法之类的特征倾向于驱动不良设计.抽象方法的全部目的是允许模式,如方法工厂,模板方法等.这些是正确使用时的良好设计模式,并且由于OO语言的多态性质量而可用.另一方面,抽象静态方法除了强制子类实现某些东西之外没有其他功能,这明显违反了几个OO设计原则(例如OCP). (4认同)
  • @Pacerier在父级中使其静态不太灵活,因为它意味着永远不允许子类使用它们的实例字段.非静态是一种更具可扩展性,可重复使用的设计.如果我需要在没有实例(通常是实用方法)的情况下调用它的方便性,我只会使它静态.如果我只打算像你提供的场景那样针对实例调用方法,我会把它变成非静态的.实例方法更灵活*即使*语言确实支持静态方法的多态,因为它不会在子类上添加不必要的约束. (4认同)
  • @Pacerier在什么理论基础上是静态的"默认"?听起来你刚刚做到了.也许在程序编程中; 实际上,我认为实例方法是默认的.无论如何,*为什么*?如果你正在调用一个实例,为什么你这么担心它是静态的呢?这有什么实际的区别? (3认同)
  • 那么为什么在文档中说明:"抽象类可能有静态字段和静态方法",而在这里它不能有一个? (2认同)
  • @ChadNB,你就快到了。再走几步,你就会看到光明。我将指导你:你的第二个选择是正确的。它将通过对象实例调用,例如“obj_child.StaticF()”。因此,您使用 Java 的经验让您思考“但它不需要是静态的”;我们给出的回应是“但它不需要是**非**静态的。” 静态是方法的默认选项。**只有引用对象实例的方法才应声明为非静态。** 例如,如果我们............ ...................................................... .. (2认同)

小智 310

语言设计不佳.直接调用静态抽象方法比仅使用该抽象方法创建实例会更有效.当使用抽象类作为枚举无法扩展的变通方法时尤其如此,这是另一个糟糕的设计示例.希望他们在下一个版本中解决这些限制.

  • Java充满了奇怪的局限性.只访问最终变量的闭包是另一个.其余的名单几乎无穷无尽.知道它们及其解决方法是Java程序员的工作.为了获得乐趣,我们必须在业余时间使用比Java更好的东西.但我不会打扰.这是取得进步的种子. (25认同)
  • 我相信你所说的"糟糕的语言设计"实际上更像是一种"保护性语言设计",其目的是限制程序员因不必要的语言特征而导致的OO原则违规. (21认同)
  • "抽象静态"概念是否违反OO原则? (21认同)
  • @threed,完全没有,但当然有人说只有"静态"本身的概念已经违反了.... (7认同)
  • 对静态的需求清楚地证明了"OO原则"并不像通常声称的那样包罗万象. (4认同)
  • @Ben 对于静力学没有绝对的**需要**。许多 OO 纯粹主义者会告诉您,静态是令人厌恶的。 (2认同)

Gar*_*ryF 143

您不能覆盖静态方法,因此将其抽象化将毫无意义.此外,在抽象类的静态方法将属于这个阶层,而不是覆盖类,因此无法使用反正.

  • @erickson - 即使没有实例,类层次结构也是完整的 - 静态方法的继承可以像实例方法的继承一样工作.Smalltalk做到了,它非常有用. (63认同)
  • 是的,在Java中无法覆盖静态方法的方式真的很遗憾. (18认同)
  • @Michel:重点是什么?如果您想要基于实例的行为,请使用实例方法. (12认同)
  • 这个答案是不正确的.抽象类中的静态方法工作正常并且是常用的.这只是类本身的静态方法可能不是抽象的.@Michel覆盖静态方法没有意义.没有实例,运行时如何知道调用哪个方法? (8认同)
  • @matiasg这根本不是什么新鲜事.总是允许抽象类具有static,_non-abstract_方法. (8认同)
  • 好消息.从java 7的官方文档(参见http://docs.oracle.com/javase/tutorial/java/IandI/abstract.html):"抽象类可能有静态字段和静态方法.您可以使用这些静态成员类引用 - 例如,AbstractClass.staticMethod() - 就像你对任何其他类一样." (4认同)

Jar*_*red 70

abstract方法的注释表明该方法必须在子类中重写.

在Java中,static子类不能覆盖成员(方法或字段)(在其他面向对象的语言中不一定如此,请参阅SmallTalk.)static成员可能被隐藏,但这与被覆盖的根本不同.

由于静态成员无法在子类中重写,abstract因此无法将注释应用于它们.

另外 - 其他语言确实支持静态继承,就像实例继承一样.从语法的角度来看,这些语言通常需要将类名包含在语句中.例如,在Java中,假设您在ClassA中编写代码,这些是等效语句(如果methodA()是静态方法,并且没有具有相同签名的实例方法):

ClassA.methodA();
Run Code Online (Sandbox Code Playgroud)

methodA();
Run Code Online (Sandbox Code Playgroud)

在SmallTalk中,类名不是可选的,因此语法是(请注意,SmallTalk不使用.来分隔"主题"和"动词",而是将其用作statemend终结符):

ClassA methodA.
Run Code Online (Sandbox Code Playgroud)

由于始终需要类名,因此始终可以通过遍历类层次结构来确定方法的正确"版本".对于它的价值,我偶尔会错过static继承,并且当我第一次开始使用Java时,由于缺乏静态继承而被咬了.另外,SmallTalk是鸭子类型(因此不支持逐个程序.)因此,它没有abstract类成员的修饰符.

  • @Steven De Groote一个静态成员实际上不能被子类覆盖.如果子类具有与超类中的静态方法具有相同签名的静态方法,则它不会覆盖它,它会隐藏它.[http://docs.oracle.com/javase/tutorial/java/IandI/override.html](http://docs.oracle.com/javase/tutorial/java/IandI/override.html)不同之处在于多态性仅适用于重写,但不适用于隐藏方法. (15认同)
  • @Steven De Groote:当您在超类本身中调用该方法时,差异就会变得明显.假设Super.foo调用Super.bar.如果子类实现Subclass.bar,然后调用foo,那么foo仍将调用Super.bar,而不是Subclass.bar.因此,你真正拥有的是两种完全不同且无关的方法,都被称为"bar".在任何有用的意义上,这都不是最重要的. (3认同)
  • “子类不能覆盖静态成员”是错误的。这是可能的,至少在 Java6 中是可能的。不确定从什么时候开始。 (2认同)
  • @ John29感谢您的澄清,但是除了命名上的区别外,它的用法似乎相似。 (2认同)
  • @Steven De Groote是的,它的用法相似,但行为不同.这就是为什么没有静态抽象方法的原因 - 如果静态抽象方法不支持多态,那么它的意义何在? (2认同)

ans*_*tta 14

我也问了同样的问题,这就是原因

从Abstract类开始说,它不会给出实现并允许子类给它

所以Subclass必须覆盖Superclass的方法,

RULE NO 1 - 无法覆盖静态方法

因为静态成员和方法是编译时元素,这就是为什么允许静态方法的重载(编译时多态)而不是覆盖(运行时多态)

所以,他们不能抽象.

没有抽象静态 <--- Java Universe中不允许的东西

  • -1,"Java不允许覆盖静态方法,因为静态成员和方法是编译时元素",这是不正确的.静态类型检查绝对可以使用`abstract static`,参见http://stackoverflow.com/questions/370962/why-cant-static-methods-be-abstract-in-java/370967#comment40304607_370967.**为什么Java不允许重写静态方法的真正原因是因为Java不允许重写静态方法. (5认同)

mom*_*omo 12

这是一个糟糕的语言设计,并没有理由为什么它不可能.

事实上,这里是如何实现CAN在做JAVA:

public class Main {

        public static void main(String[] args) {
                // This is done once in your application, usually at startup
                Request.setRequest(new RequestImplementationOther());

                Request.doSomething();
        }

        public static final class RequestImplementationDefault extends Request {
                @Override
                void doSomethingImpl() {
                        System.out.println("I am doing something AAAAAA");
                }
        }

        public static final class RequestImplementaionOther extends Request {
                @Override
                void doSomethingImpl() {
                        System.out.println("I am doing something BBBBBB");
                }
        }

        // Static methods in here can be overriden
        public static abstract class Request {

                abstract void doSomethingImpl();

                // Static method
                public static void doSomething() {
                        getRequest().doSomethingImpl();
                }

                private static Request request;
                private static Request getRequest() {
                        // If setRequest is never called prior, it will default to a default implementation. Of course you could ignore that too. 
                        if ( request == null ) {
                                return request = new RequestImplementationDefault();
                        }
                        return request;
                }
                public static Request setRequest(Request r){
                        return request = r;
                }

        }
}
Run Code Online (Sandbox Code Playgroud)

=================下面的旧例子=================

查找getRequest和getRequestImpl ...可以在调用之前调用setInstance来改变实现.

import java.io.IOException;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.HttpSession;

import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;

/**
 * @author Mo. Joseph
 * @date 16 mar 2012
 **/

public abstract class Core {


    // ---------------------------------------------------------------        
    private static Core singleton; 
    private static Core getInstance() {
        if ( singleton == null )
            setInstance( new Core.CoreDefaultImpl() );  // See bottom for CoreDefaultImpl

        return singleton;
    }    

    public static void setInstance(Core core) {
        Core.singleton = core;
    }
    // ---------------------------------------------------------------        



    // Static public method
    public static HttpServletRequest getRequest() {      
        return getInstance().getRequestImpl();
    }


    // A new implementation would override this one and call setInstance above with that implementation instance
    protected abstract HttpServletRequest getRequestImpl();




    // ============================ CLASSES =================================

    // ======================================================================
    // == Two example implementations, to alter getRequest() call behaviour 
    // == getInstance() have to be called in all static methods for this to work
    // == static method getRequest is altered through implementation of getRequestImpl
    // ======================================================================

    /** Static inner class CoreDefaultImpl */
    public static class CoreDefaultImpl extends Core { 
        protected HttpServletRequest getRequestImpl() {
            return ((ServletRequestAttributes) RequestContextHolder.getRequestAttributes()).getRequest();
        }
    }

     /** Static inner class CoreTestImpl : Alternative implementation */
    public static class CoreTestImpl extends Core { 
        protected HttpServletRequest getRequestImpl() {
            return new MockedRequest();
        }
    }       

}
Run Code Online (Sandbox Code Playgroud)

  • 我不明白你在哪里提供了问题中提到的**"抽象静态"**方法的例子,你用粗体写的**可以用JAVA**.这完全是错误的. (5认同)
  • 它本身不允许您将其定义为抽象静态,但您可以实现类似的结果,您可以使用此模式/黑客更改静态方法的实现。这几乎不会被误导。这是可以做到的,但使用不同的语义。 (2认同)
  • @ScubaSteve首先,您的结论是错误的。其次,它达到相同的结果。意味着可以通过另一种实现方式更改对类的静态访问。这并不是说我使static关键字可抽象化的答案,但是使用此模式,您可以使用static方法,并且仍然可以更改其实现。虽然它确实仅具有全球化的负面影响,但是对于测试/生产/开发环境,它却为我们提供了诀窍。 (2认同)

Rah*_*ena 5

  • 仅定义抽象方法,以便可以在子类中重写它.但是,无法覆盖静态方法.因此,具有抽象的静态方法是编译时错误.

    现在接下来的问题是为什么静态方法不能被覆盖?

  • 这是因为静态方法属于特定类而不属于其实例.如果您尝试覆盖静态方法,则不会得到任何编译或运行时错误,但编译器只会隐藏超类的静态方法.


And*_*kov 5

根据定义,静态方法不需要知道this. 因此,它不能是虚方法(根据通过 可用的动态子类信息重载this);相反,静态方法重载完全基于编译时可用的信息(这意味着:一旦您引用超类的静态方法,您即调用超类方法,而不是子类方法)。

据此,抽象静态方法将毫无用处,因为您永远不会将其引用替换为某个已定义的主体。