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

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

你不能重写静态方法,所以使它抽象是没有意义的。此外,抽象类中的静态方法将属于该类,而不是覆盖类,因此无论如何都不能使用。


因为“抽象”的意思是:“不实现任何功能”,而“静态”的意思是:“即使你没有对象实例,也有功能”。这是一个逻辑矛盾。


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

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

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

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


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

在Java中,静态成员(方法或字段)不能被子类覆盖(在其他面向对象语言中不一定是这样,请参阅SmallTalk)。静态成员可能被隐藏,但这与被覆盖有本质区别。

由于静态成员不能在子类中被重写,因此抽象注释不能应用于它们。

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

ClassA.methodA();

and

methodA();

在SmallTalk中,类名不是可选的,所以语法是(注意,SmallTalk不使用。将“主语”和“动词”分开,而是将其用作语句结束符):

ClassA methodA.

因为总是需要类名,所以总是可以通过遍历类层次结构来确定方法的正确“版本”。无论如何,我偶尔会错过静态继承,当我第一次开始使用Java时,我就被Java中缺乏静态继承所困扰。此外,SmallTalk是duck类型的(因此不支持契约式编程)。因此,类成员没有抽象修饰符。


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


这是一个糟糕的语言设计,真的没有理由不可能。

事实上,这里有一个模式或方法可以在**Java中模仿它,让你至少能够修改自己的实现:

public static abstract class Request {                 

        // Static method
        public static void doSomething() {
                get().doSomethingImpl();
        }
        
        // Abstract method
        abstract void doSomethingImpl();

        /////////////////////////////////////////////
        private static Request SINGLETON;
        private static Request get() {
            if ( SINGLETON == null ) {
                // If set(request) is never called prior,
                // it will use a default implementation. 
                return SINGLETON = new RequestImplementationDefault();
            }
            return SINGLETON;
        }
        public static Request set(Request instance){
            return SINGLETON = instance;
        }
        /////////////////////////////////////////////
}

两种实现:

/////////////////////////////////////////////////////

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

/////////////////////////////////////////////////////

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

/////////////////////////////////////////////////////

可以这样使用:

Request.set(new RequestImplementationDefault());

// Or

Request.set(new RequestImplementationTest());

// Later in the application you might use

Request.doSomething();

这将允许您静态地调用方法,同时还能够更改例如Test环境的实现。

理论上,您也可以在ThreadLocal上执行此操作,并且能够为每个线程上下文设置实例,而不是像这里所示的完全全局,然后可以执行Request。withRequest(anotherRequestImpl,() ->{…})或类似的。

现实世界通常不需要ThreadLocal方法,通常能够全局地改变测试环境的实现就足够了。

请注意,这样做的唯一目的是能够保留静态方法所提供的直接、轻松和干净地调用方法的能力,同时能够切换实现,以牺牲稍微复杂的实现为代价。

它只是一种绕过通常不可修改的静态代码的模式。


An abstract method is defined only so that it can be overridden in a subclass. However, static methods can not be overridden. Therefore, it is a compile-time error to have an abstract, static method. Now the next question is why static methods can not be overridden?? It's because static methods belongs to a particular class and not to its instance. If you try to override a static method you will not get any compilation or runtime error but compiler would just hide the static method of superclass.


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


我也问了同样的问题,原因如下

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

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

规则1 -静态方法不能被覆盖

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

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

Java宇宙中不允许有抽象静态<——之类的东西


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

通过这种方式,您可以创建sortableObject抽象类甚至接口 使用(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> 

它可以检索类型,构建一个弹出菜单来选择要排序的类型,并通过从该类型获取数据来返回列表,以及hainv一个add函数,当选择了排序类型时,可以自动对新项进行排序。 注意SortableList实例可以直接访问“T”的静态方法:

String [] MenuItems = T.getSortableTypes();

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

再见 奥拉夫。


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

这是关于它的官方文件:

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


首先,关于抽象类的一个关键点—— 抽象类不能被实例化(参见wiki)。因此,您不能创建抽象类的任何实例。

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

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

繁荣。


假设有两个类,父类和子类。父母是抽象的。声明如下:

abstract class Parent {
    abstract void run();
}

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

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

但是,现在假设Parent不是抽象的。

class Parent {
    static void run() {}
}

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

抽象方法的定义是“声明但未实现的方法”,这意味着它本身不返回任何东西。

静态方法的定义是“对于相同的参数返回相同值的方法,而不管调用它的实例是什么”。

抽象方法的返回值会随着实例的改变而改变。静态方法则不会。静态抽象方法基本上是这样一种方法,它的返回值是常量,但不返回任何东西。这是一个逻辑矛盾。

同样,使用静态抽象方法的理由也不多。


根据Java文档:

静态方法是与其中的类相关联的方法 它是定义的,而不是与任何对象。类的每个实例 共享它的静态方法

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

一个很好的例子是:

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));
    }    
}

抽象类不能有静态方法,因为抽象是为了实现DYNAMIC BINDING,而静态方法是静态绑定到它们的功能上的。静态方法的意思是 行为不依赖于实例变量,因此没有实例/对象 是必需的。只是上课而已。静态方法属于类而不是对象。 它们存储在一个称为PERMGEN的内存区域中,每个对象都从这里共享它们。 抽象类中的方法动态地绑定到它们的功能上。


我相信我已经找到了这个问题的答案,即为什么接口的方法(就像父类中的抽象方法一样工作)不能是静态的。以下是完整的答案(不是我的)

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

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

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

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

然后任意Foo.bar();调用显然是非法的,您将始终使用Foo2.bar();。

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

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

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


因为“抽象”意味着方法是要被重写的,而不能重写“静态”方法。


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

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


根据定义,静态方法不需要知道这些。因此,它不能是一个虚方法(根据通过this获得的动态子类信息重载);相反,静态方法重载仅基于编译时可用的信息(这意味着:一旦引用了父类的静态方法,就调用父类方法,而不调用子类方法)。

根据这一点,抽象静态方法将是非常无用的,因为您永远不会用一些已定义的对象来代替它的引用。


我看到已经有无数的答案,但我没有看到任何实际的解决方案。当然,这是一个真正的问题,没有很好的理由在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
    }
}

C1和C2中的doWork()方法相同。可能有很多这样的类:C3 C4等。如果允许静态抽象,你可以通过这样做来消除重复代码:

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
    }
}

但这将无法编译,因为静态抽象组合是不允许的。 然而,这可以通过静态类构造来避免,这是允许的:

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

因为abstract是应用于abstract方法上的关键字,所以abstract方法不指定主体。而静态关键字则属于类区域。


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


将一个方法声明为静态意味着我们可以通过它的类名调用该方法,如果这个类也是抽象的,那么调用它就没有意义了,因为它不包含任何主体,因此我们不能同时将一个方法声明为静态的和抽象的。


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

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

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


因为抽象方法总是需要通过子类来实现。但是如果你将任何方法设置为静态,那么就不可能重写这个方法

例子

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
}

有一种情况是静态和抽象可以一起使用,那就是当这两个修饰符都放在嵌套类的前面时。


静态方法 可以调用静态方法,而不需要创建类的实例。静态方法属于类,而不是类的对象。 静态方法可以访问静态数据成员,也可以改变它的值。 摘要关键字用于实现抽象。 静态方法不能在子类中重写或实现。因此,把静态方法做得抽象是没有用的。


在一行中,这种危险的组合(抽象+静态)违反了面向对象的原则,即多态性。

在继承情况下,JVM将在运行时根据实现决定实例的类型(运行时多态性),而不是根据引用变量的类型(编译时多态性)。

@Overriding:

静态方法不支持@ override(运行时多态性),而只支持方法隐藏(编译时多态性)。

@Hiding:

但是在抽象静态方法的情况下,父(抽象)类没有方法的实现。因此,子类型引用是唯一可用的,而且它不是多态性。

子引用是唯一可用的:

出于这个原因(抑制oop特性),Java语言认为抽象+静态是非法(危险)的方法组合。