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

时间:2008-12-16 10:45:28

标签: java abstract-class static-methods

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

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

26 个答案:

答案 0 :(得分:533)

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

答案 1 :(得分:293)

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

答案 2 :(得分:140)

您不能覆盖静态方法,因此将其抽象化将毫无意义。而且,抽象类中的静态方法属于该类,而不是覆盖类,因此无论如何都不能使用。

答案 3 :(得分:67)

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

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

由于无法在子类中覆盖静态成员,因此abstract注释无法应用于它们。

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

ClassA.methodA();

methodA();

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

ClassA methodA.

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

答案 4 :(得分:14)

我也问了同样的问题,这就是为什么

自Abstract类说,它不会给出实现并允许子类给它

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

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

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

所以,他们不能抽象。

没有像抽象静态&lt; --- Java Universe中不允许的

答案 5 :(得分:10)

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

事实上,这是一个关于如何 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;
                }

        }
}

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

查找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();
        }
    }       

}

使用如下:

static {
     Core.setSingleton(new Core.CoreDefaultImpl());

     // Or

     Core.setSingleton(new Core.CoreTestImpl());

     // Later in the application you might use

     Core.getRequest().doSomething(); 

}

答案 6 :(得分:5)

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

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

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

答案 7 :(得分:3)

我看到已经有了数以万计的答案,但我没有看到任何实际的解决方案。当然这是一个真正的问题,没有充分的理由在Java中排除这种语法。由于最初的问题缺乏可能需要的背景,我提供了背景和解决方案:

假设在一堆相同的类中有静态方法。这些方法调用特定于类的静态方法:

class C1 {
    static void doWork() {
        ...
        for (int k: list)
            doMoreWork(k);
        ...
    }
    private static void doMoreWork(int k) {
        // code specific to class C1
    }
}
class C2 {
    static void doWork() {
        ...
        for (int k: list)
            doMoreWork(k);
        ...
    }
    private static void doMoreWork(int k) {
        // code specific to class C2
    }
}
doWork()C1中的

C2方法相同。可能有很多这样的calsses:C3 C4等。如果允许static abstract,您可以通过执行以下操作来消除重复的代码:

abstract class C {
    static void doWork() {
        ...
        for (int k: list)
            doMoreWork(k);
        ...
    }

    static abstract void doMoreWork(int k);
}

class C1 extends C {
    private static void doMoreWork(int k) {
        // code for class C1
    }
}

class C2 extends C {
    private static void doMoreWork(int k) {
        // code for class C2
    }
}

但这不会编译,因为不允许static abstract组合。 但是,这可以通过static class构造来规避,这是允许的:

abstract class C {
    void doWork() {
        ...
        for (int k: list)
            doMoreWork(k);
        ...
    }
    abstract void doMoreWork(int k);
}
class C1 {
    private static final C c = new  C(){  
        @Override void doMoreWork(int k) {
            System.out.println("code for C1");
        }
    };
    public static void doWork() {
        c.doWork();
    }
}
class C2 {
    private static final C c = new C() {
        @Override void doMoreWork(int k) {
            System.out.println("code for C2");
        }
    };
    public static void doWork() {
        c.doWork();
    }
}

使用此解决方案,唯一重复的代码是

    public static void doWork() {
        c.doWork();
    }

答案 8 :(得分:3)

根据定义,静态方法不需要知道this。因此,它不能是一个虚方法(根据this提供的动态子类信息重载);相反,静态方法重载完全基于编译时可用的信息(这意味着:一旦引用超类的静态方法,就称为超类方法,但从不调用子类方法)。

根据这一点,抽象的静态方法将毫无用处,因为你永远不会将它的引用替换为某个已定义的主体。

答案 9 :(得分:2)

假设有两个类ParentChildParentabstract。声明如下:

abstract class Parent {
    abstract void run();
}

class Child extends Parent {
    void run() {}
}

这意味着Parent的任何实例都必须指定run()的执行方式。

但是,现在假设Parent不是abstract

class Parent {
    static void run() {}
}

这意味着Parent.run()将执行静态方法。

abstract方法的定义是“声明但未实现的方法”,这意味着它本身不会返回任何内容。

static方法的定义是“为相同参数返回相同值的方法,无论它被调用的实例如何”。

abstract方法的返回值将随实例的变化而变化。 static方法不会。 static abstract方法几乎是一种返回值为常量但不返回任何内容的方法。这是一个合乎逻辑的矛盾。

此外,static abstract方法确实没有多大理由。

答案 10 :(得分:2)

抽象类不能有静态方法,因为抽象是为了实现DYNAMIC BINDING,而静态方法是静态绑定到它们的功能。静态方法意味着 行为不依赖于实例变量,因此没有实例/对象 是必需的。只是class.Static方法属于类而不是对象。 它们存储在一个称为PERMGEN的存储区中,与每个对象共享。 抽象类中的方法被动态绑定到它们的功能。

答案 11 :(得分:1)

因为抽象方法总是需要通过子类实现。但是如果您将任何方法设为静态,则无法覆盖此方法

示例

abstract class foo {
    abstract static void bar2(); 
}


class Bar extends foo {
    //in this if you override foo class static method then it will give error
}

答案 12 :(得分:1)

由于抽象方法属于该类并且不能被实现类覆盖。即使存在具有相同签名的静态方法,它也会隐藏该方法而不覆盖它。 因此,将抽象方法声明为静态方法并不重要,因为它将永远无法获取主体。因此,编译时错误。

答案 13 :(得分:1)

因为抽象类是OOPS的概念,而静态成员不是OOPS的一部分。...
现在,我们可以在接口中声明静态的完整方法,并且可以通过在接口内部声明main方法来执行接口

interface Demo 
{
  public static void main(String [] args) {
     System.out.println("I am from interface");
  }
}

答案 14 :(得分:1)

我相信我找到了这个问题的答案,其形式是为什么界面的方法(像父类中的抽象方法一样工作)不能是静态的。 Here is the full answer (not mine)

基本上静态方法可以在编译时绑定,因为要调用它们,您需要指定一个类。这与实例方法不同,在编译时,您从中调用方法的引用类可能是未知的(因此调用哪个代码块只能在运行时确定)。

如果你正在调用一个静态方法,你已经知道它所实现的类,或者它的任何直接子类。如果你定义

abstract class Foo {
    abstract static void bar();
}

class Foo2 {
    @Override
    static void bar() {}
}

然后,任何Foo.bar();电话显然都是非法的,您将始终使用Foo2.bar();

考虑到这一点,静态抽象方法的唯一目的是强制子类来实现这样的方法。您可能最初认为这是非常错误的,但如果您有一个泛型类型参数<E extends MySuperClass>,那么保证E可以.doSomething()的via接口会很好。请记住,由于类型擦除,泛型只存在于编译时。

那么,它会有用吗?是的,也许这就是为什么Java 8允许接口中的静态方法(尽管只有默认实现)。为什么不在类中使用默认实现抽象静态方法?仅仅因为具有默认实现的抽象方法实际上是一种具体方法。

为什么不抽象/接口静态方法没有默认实现?显然,仅仅是因为Java识别它必须执行哪个代码块的方式(我的答案的第一部分)。

答案 15 :(得分:1)

可以在没有类实例的情况下调用静态方法。在您的示例中,您可以调用foo.bar2(),但不能调用foo.bar(),因为对于bar,您需要一个实例。 以下代码可以工作:

foo var = new ImplementsFoo();
var.bar();

如果调用静态方法,它将始终执行相同的代码。在上面的示例中,即使您在ImplementsFoo中重新定义bar2,对var.bar2()的调用也会执行foo.bar2()。

如果bar2现在没有实现(这就是抽象的意思),你可以调用一个没有实现的方法。那是非常有害的。

答案 16 :(得分:0)

根据Java doc

  

静态方法是与其中的类关联的方法   它被定义而不是任何对象。班级的每个实例   分享其静态方法

在Java 8中,除了默认方法外,接口中也允许使用静态方法。这使我们更容易在库中组织辅助方法。我们可以在同一个接口中保留特定于接口的静态方法,而不是在单独的类中。

一个很好的例子是:

list.sort(ordering);

而不是

Collections.sort(list, ordering);

doc本身也提供了另一个使用静态方法的例子:

public interface TimeClient {
    // ...
    static public ZoneId getZoneId (String zoneString) {
        try {
            return ZoneId.of(zoneString);
        } catch (DateTimeException e) {
            System.err.println("Invalid time zone: " + zoneString +
                "; using default time zone instead.");
            return ZoneId.systemDefault();
        }
    }

    default public ZonedDateTime getZonedDateTime(String zoneString) {
        return ZonedDateTime.of(getLocalDateTime(), getZoneId(zoneString));
    }    
}

答案 17 :(得分:0)

因为&#39;摘要&#39;意味着该方法旨在被覆盖,并且不能覆盖静态&#39;方法

答案 18 :(得分:0)

当常规方法被子类覆盖并提供功能时,它们可以是抽象的。 想象一下,类FooBar1, Bar2, Bar3等扩展。因此,每个人都会根据自己的需要拥有自己的抽象类版本。

现在,定义的静态方法属于类,它们与类的对象或其子类的对象无关。它们甚至不需要它们存在,它们可以在不实例化类的情况下使用。因此,它们需要准备就绪,不能依赖子类来为它们添加功能。

答案 19 :(得分:0)

首先,关于抽象类的关键点 - 无法实例化抽象类(请参阅wiki)。因此,您无法创建任何抽象类的实例。

现在,java处理静态方法的方法是通过与该类的所有实例共享方法。

所以,如果你不能实例化一个类,那么该类就不能有抽象的静态方法,因为抽象方法需要扩展。

动臂。

答案 20 :(得分:0)

您可以使用Java 8中的接口执行此操作。

这是关于它的官方文件:

https://docs.oracle.com/javase/tutorial/java/IandI/defaultmethods.html

答案 21 :(得分:0)

因为abstract是一个应用于抽象方法的关键字,所以不指定正文。如果我们谈论静态关键字,它属于类区域。

答案 22 :(得分:0)

因为如果你在类中使用任何静态成员或静态变量,它将在类加载时加载。

答案 23 :(得分:0)

将方法声明为static意味着我们可以通过类名调用该方法,如果该类也是abstract,则调用它是没有意义的,因为它不包含任何主体,因此我们无法将方法声明为staticabstract

答案 24 :(得分:0)

使用抽象静态方法的想法是你不能直接为该方法使用那个特定的抽象类,但只允许一阶导数实现那个静态方法(或者对于泛型:实际的类您使用的通用)。

这样,您可以创建一个sortableObject抽象类甚至接口 with(auto-)抽象静态方法,定义排序选项的参数:

public interface SortableObject {
    public [abstract] static String [] getSortableTypes();
    public String getSortableValueByType(String type);
}

现在您可以定义一个可排序对象,该对象可以按所有这些对象相同的主要类型进行排序:

public class MyDataObject implements SortableObject {
    final static String [] SORT_TYPES = {
        "Name","Date of Birth"
    }
    static long newDataIndex = 0L ;

    String fullName ;
    String sortableDate ;
    long dataIndex = -1L ;
    public MyDataObject(String name, int year, int month, int day) {
        if(name == null || name.length() == 0) throw new IllegalArgumentException("Null/empty name not allowed.");
        if(!validateDate(year,month,day)) throw new IllegalArgumentException("Date parameters do not compose a legal date.");
        this.fullName = name ;
        this.sortableDate = MyUtils.createSortableDate(year,month,day);
        this.dataIndex = MyDataObject.newDataIndex++ ;
    }
    public String toString() {
        return ""+this.dataIndex+". "this.fullName+" ("+this.sortableDate+")";
    }

    // override SortableObject 
    public static String [] getSortableTypes() { return SORT_TYPES ; }
    public String getSortableValueByType(String type) {
        int index = MyUtils.getStringArrayIndex(SORT_TYPES, type);
        switch(index) {
             case 0: return this.name ;
             case 1: return this.sortableDate ;
        }
        return toString(); // in the order they were created when compared
    }
}

现在你可以创建一个

public class SortableList<T extends SortableObject> 

可以检索类型,构建弹出菜单以选择要排序的类型并通过从该类型获取数据来求助列表,以及在选择排序类型时添加一个添加函数,可以自动排序新项目。 请注意,SortableList的实例可以直接访问静态方法“T”:

String [] MenuItems = T.getSortableTypes();

必须使用实例的问题是SortableList可能还没有项目,但已经需要提供首选排序。

Cheerio, 奥拉夫。

答案 25 :(得分:-1)

因为如果一个类扩展了一个抽象类,那么它必须覆盖抽象方法,这是必需的。因为静态方法是在编译时解析的类方法,而重写的方法是在运行时解析并遵循动态多态的实例方法。