Почему статические методы не могут быть абстрактными в Java - PullRequest
557 голосов
/ 16 декабря 2008

Вопрос в Java, почему я не могу определить абстрактный статический метод? например

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

Ответы [ 24 ]

534 голосов
/ 16 декабря 2008

Потому что «абстрактный» означает: «не реализует никакой функциональности», а «статический» означает: «есть функциональность, даже если у вас нет экземпляра объекта» И это логическое противоречие.

293 голосов
/ 20 октября 2009

Плохой языковой дизайн. Было бы гораздо эффективнее напрямую вызывать статический абстрактный метод, чем создавать экземпляр только для использования этого абстрактного метода. Особенно актуально при использовании абстрактного класса в качестве обходного пути для невозможности расширения перечисления, что является еще одним плохим примером проектирования. Надеюсь, что они устранят эти ограничения в следующем выпуске.

140 голосов
/ 16 декабря 2008

Вы не можете переопределить статический метод, поэтому делать его абстрактным было бы бессмысленно. Более того, статический метод в абстрактном классе должен принадлежать этому классу, а не переопределяющему классу, поэтому его нельзя использовать в любом случае.

67 голосов
/ 16 декабря 2008

Примечание abstract к методу указывает, что метод ДОЛЖЕН быть переопределен в подклассе.

В Java элемент static (метод или поле) не может быть переопределен подклассами (это не обязательно верно в других объектно-ориентированных языках, см. SmallTalk.) Элемент static может быть скрытым , но это принципиально отличается от переопределено .

Поскольку статические члены не могут быть переопределены в подклассе, аннотация abstract не может быть применена к ним.

Кроме того, другие языки поддерживают статическое наследование, как и наследование экземпляров. С точки зрения синтаксиса эти языки обычно требуют, чтобы имя класса было включено в оператор. Например, в Java, если вы пишете код в ClassA, это эквивалентные операторы (если methodA () является статическим методом, и нет метода экземпляра с такой же сигнатурой):

ClassA.methodA();

и

methodA();

В SmallTalk имя класса не является обязательным, поэтому синтаксис такой (обратите внимание, что SmallTalk не использует. Для разделения «субъекта» и «глагола», а вместо этого использует его в качестве терминатора исправления состояния):

ClassA methodA.

Поскольку имя класса всегда требуется, правильная «версия» метода всегда может быть определена путем обхода иерархии классов. Что бы это ни стоило, я иногда пропускаю static наследование, и меня укусило отсутствие статического наследования в Java, когда я впервые начал с ним. Кроме того, SmallTalk типизирован по типу утки (и поэтому не поддерживает программу по контракту.) Таким образом, он не имеет модификатора abstract для членов класса.

14 голосов
/ 15 мая 2013

Я тоже задавал тот же вопрос, вот почему

Поскольку класс Abstract говорит, он не даст реализацию и не позволит подклассу дать его

поэтому подкласс должен переопределить методы суперкласса,

ПРАВИЛО № 1 - Статический метод не может быть переопределен

Поскольку статические члены и методы являются элементами времени компиляции, поэтому допускается перегрузка (полиморфизм времени компиляции) статических методов, а не переопределение (полиморфизм времени выполнения)

Итак, они не могут быть абстрактными.

Нет такой вещи, как abstract static <--- Не допускается в Java Universe </strong>

11 голосов
/ 19 марта 2012

Это ужасный языковой дизайн, и на самом деле нет причин, почему это невозможно.

Фактически, вот реализация того, как это МОЖЕТ быть сделано в 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(); 

}
5 голосов
/ 22 сентября 2012
  • Абстрактный метод определяется только для того, чтобы его можно было переопределить в подклассе. Тем не менее, статические методы не могут быть переопределены. Следовательно, ошибка времени компиляции - иметь абстрактный статический метод.

    Теперь следующий вопрос: почему статические методы нельзя переопределить?

  • Это потому, что статические методы принадлежат конкретному классу, а не его экземпляру. Если вы попытаетесь переопределить статический метод, вы не получите никакой ошибки компиляции или времени выполнения, но компилятор просто скрыт статический метод суперкласса.

3 голосов
/ 05 декабря 2015

Статический метод по определению не должен знать this. Таким образом, это не может быть виртуальный метод (который перегружен в соответствии с информацией динамического подкласса, доступной через this); вместо этого перегрузка статического метода основана исключительно на информации, доступной во время компиляции (это означает, что когда вы ссылаетесь на статический метод суперкласса, вы вызываете именно метод суперкласса, но не метод подкласса).

В соответствии с этим абстрактные статические методы были бы совершенно бесполезны, потому что у вас никогда не будет ссылки, замененной каким-либо определенным телом.

3 голосов
/ 20 января 2016

Я вижу, что уже есть миллионы ответов, но я не вижу практических решений. Конечно, это реальная проблема, и нет веских причин для исключения этого синтаксиса в 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();
    }
2 голосов
/ 15 июня 2015

Предположим, есть два класса, Parent и Child. Parent - это abstract. Заявления следующие:

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 нет особой причины.

...