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

Why can't static methods be abstract in Java

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

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


因为"abstract"的意思是"不实现任何功能","static"的意思是"即使没有对象实例也有功能"。这是一个逻辑上的矛盾。


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


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


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

在Java中,EDCOX1×1成员(方法或字段)不能被子类重写(这在其他面向对象语言中不一定是正确的,参见SimalTalk)。EDOCX1×1成员可能被隐藏,但与重写相比根本不同。

由于静态成员不能在子类中被重写,因此不能对其应用abstract批注。

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

1
ClassA.methodA();

1
methodA();

在smalltalk中,类名不是可选的,因此语法是(注意smalltalk不使用)。将"主语"和"动词"分开,但将其用作Statemend终结符):

1
ClassA methodA.

因为类名称总是必需的,所以始终可以通过遍历类层次结构来确定方法的正确"版本"。因为它的价值,我偶尔会错过EDCOX1的1继承,并且在我第一次使用Java时被Java中缺少静态继承所咬。此外,smalltalk是duck类型的(因此不支持契约式的程序),因此它没有类成员的abstract修饰符。


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

因为抽象类会说,它不会给出实现并允许子类给出它

所以子类必须重写超类的方法,

规则1-不能重写静态方法

因为静态成员和方法是编译时元素,所以允许静态方法的重载(编译时多态性),而不是重写(运行时多态性)。

所以,它们不能是抽象的。

在Java宇宙中没有任何抽象抽象的东西。


这是一个糟糕的语言设计,真的没有理由解释为什么它是不可能的。

事实上,这里有一个关于如何在Java中实现的实现:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
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来更改实现。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
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();
        }
    }      






}


  • 抽象方法的定义只能使其在子类中被重写。但是,不能重写静态方法。因此,拥有一个抽象的静态方法是一个编译时错误。

    现在,下一个问题是为什么静态方法不能被重写??

  • 这是因为静态方法属于一个特定的类,而不是它的实例。如果您试图重写一个静态方法,您将不会得到任何编译或运行时错误,但编译器只会隐藏超类的静态方法。


我看到上帝已经有无数的答案,但我看不出任何实际的解决办法。当然,这是一个真正的问题,在Java中没有排除这种语法的充分理由。由于最初的问题缺少可能需要的上下文,因此我提供了上下文和解决方案:

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

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
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
    }
}

C1C2中的doWork()方法相同。可能有很多这样的错误:C3C4等。如果允许static abstract的话,你可以通过如下方式消除重复代码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
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构造来规避,允许:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
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();
    }
}

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

1
2
3
    public static void doWork() {
        c.doWork();
    }


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

1
2
3
4
5
6
7
abstract class Parent {
    abstract void run();
}

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

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

但是,现在假设Parent不是abstract

1
2
3
class Parent {
    static void run() {}
}

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

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

static方法的定义是"对相同参数返回相同值的方法,而不考虑调用它的实例"。

abstract方法的返回值将随着实例的更改而更改。static方法不会。static abstract方法基本上是返回值为常量但不返回任何内容的方法。这是一个合乎逻辑的矛盾。

另外,使用static abstract方法的原因确实不多。


根据定义,静态方法不需要知道this。因此,它不能是虚拟方法(即根据this中提供的动态子类信息进行重载);而静态方法重载只能基于编译时可用的信息(这意味着:一旦引用了超类的静态方法,就称为超类方法,而不是子类方法)。

根据这一点,抽象静态方法将是非常无用的,因为您永远不会让它的引用被某个已定义的主体替换。


抽象类不能有静态方法,因为抽象是为了实现动态绑定而进行的,而静态方法是静态绑定到它们的功能上的。行为不依赖实例变量,因此没有实例/对象是必需的。只有类。静态方法属于类,而不是对象。它们存储在一个称为permgen的内存区域中,在该区域中它与每个对象共享。抽象类中的方法动态绑定到其功能。


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

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

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

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


我相信我已经找到了这个问题的答案,其形式是为什么接口的方法(类似于父类中的抽象方法)不能是静态的。这是完整的答案(不是我的)

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

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

1
2
3
4
5
6
7
8
abstract class Foo {
    abstract static void bar();
}

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

那么任何Foo.bar();调用显然都是非法的,您将始终使用Foo2.bar();

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

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

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


因为抽象类是一个oops概念,静态成员不是oops的一部分…现在的问题是,我们可以在接口中声明静态的完整方法,并且可以通过在接口中声明主方法来执行接口。

1
2
3
4
5
6
interface Demo
{
  public static void main(String [] args) {
     System.out.println("I am from interface");
  }
}


拥有一个抽象静态方法的想法是,您不能直接为该方法使用特定的抽象类,但只允许第一个派生实现该静态方法(或者对于泛型:您使用的泛型的实际类)。

这样,您就可以创建例如SortableObject抽象类甚至接口使用(auto-)抽象静态方法,该方法定义排序选项的参数:

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

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

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
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
    }
}

现在您可以创建一个

1
public class SortableList<T extends SortableObject>

它可以检索类型,构建一个弹出菜单来选择要排序的类型,并通过从该类型获取数据来使用列表,以及一个添加函数,当选择了排序类型时,该函数可以自动对新项目排序。注意SortableList实例可以直接访问静态方法"t":

1
String [] MenuItems = T.getSortableTypes();

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

再见,奥拉夫。


首先,抽象类的一个关键点-无法实例化抽象类(请参见wiki)。因此,不能创建抽象类的任何实例。

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

因此,如果您不能实例化一个类,那么该类就不能有抽象静态方法,因为抽象方法要求扩展。

繁荣。


根据Java文档:

A static method is a method that is associated with the class in which
it is defined rather than with any object. Every instance of the class
shares its static methods

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

一个很好的例子是:

1
list.sort(ordering);

而不是

1
Collections.sort(list, ordering);

另一个使用静态方法的例子也在文档本身中给出:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
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));
    }    
}

因为"abstract"意味着要重写方法,而不能重写"static"方法。


当常规方法被子类覆盖并提供功能时,它们可以是抽象的。假设类Foo是由Bar1, Bar2, Bar3等扩展的,那么每个类都将根据需要有自己的抽象类版本。

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


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


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


声明一个方法为static意味着我们可以用它的类名来调用这个方法,如果这个类也是abstract的话,调用它是没有意义的,因为它不包含任何主体,因此我们不能同时声明一个方法为staticabstract


可以用Java 8中的接口实现这一点。

这是关于它的官方文件:

https://docs.oracle.com/javase/tutorial/java/iandi/defaultmethods.html网站


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