Почему статические методы не могут быть абстрактными в Java

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

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

Ответы

Ответ 1

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

Ответ 2

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

Ответ 3

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

Ответ 4

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

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

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

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

ClassA.methodA();

и

methodA();

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

ClassA methodA.

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

Ответ 5

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

Поскольку абстрактный класс говорит, он не даст реализации и позволит подклассу дать ему

поэтому Subclass должен переопределить методы Superclass,

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

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

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

Нет такой вещи, как абстрактный static < --- Не разрешено в Java Universe

Ответ 6

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

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

}

Ответ 7

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

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

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

Ответ 8

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

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

Ответ 9

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

Ответ 10

Предположим, что существуют два класса: 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 изменится по мере изменения экземпляра. A static метод не будет. Метод static abstract - это в значительной степени метод, в котором возвращаемое значение является постоянным, но ничего не возвращает. Это логическое противоречие.

Кроме того, для метода static abstract действительно не так уж и много.

Ответ 11

Абстрактный класс не может иметь статический метод, потому что абстракция выполняется для достижения DYNAMIC BINDING, тогда как статические методы статически привязаны к их функциональности. Статический метод означает поведение не зависит от переменной экземпляра, поэтому нет экземпляра/объекта требуется. Просто класс. Статические методы относятся к классу, а не к объекту. Они хранятся в области памяти, известной как PERMGEN, из которой она делится с каждым объектом. Методы в абстрактном классе динамически привязаны к их функциональности.

Ответ 12

Статический метод можно вызывать без экземпляра класса. В вашем примере вы можете вызвать foo.bar2(), но не foo.bar(), потому что для бара вам нужен экземпляр. Следующий код будет работать:

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

Если вы вызываете статический метод, он будет всегда исполнять тот же код. В приведенном выше примере, даже если вы переопределите bar2 в ImplementsFoo, вызов var.bar2() будет выполнять foo.bar2().

Если bar2 теперь не имеет реализации (что абстрактное означает), вы можете вызвать метод без реализации. Это очень вредно.

Ответ 13

Я считаю, что нашел ответ на этот вопрос в форме того, почему методы интерфейса (которые работают как абстрактные методы в родительском классе) не могут быть статичными. Вот полный ответ (не мой)

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

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

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

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

Тогда любой вызов Foo.bar();, очевидно, является незаконным, и вы всегда будете использовать Foo2.bar();.

С учетом этого единственной целью статического абстрактного метода было бы принудительное применение подклассов для реализации такого метода. Первоначально вы могли подумать, что это ОЧЕНЬ неправильно, но если у вас есть типичный параметр типа <E extends MySuperClass>, было бы неплохо гарантировать через интерфейс, что E может .doSomething(). Имейте в виду, что из-за генераторов стираемого типа существует только во время компиляции.

Итак, было бы полезно? Да, и, возможно, именно поэтому Java 8 разрешает статические методы в интерфейсах (хотя и только с реализацией по умолчанию). Почему бы не абстрактно статические методы с реализацией по умолчанию в классах? Просто потому, что абстрактный метод с реализацией по умолчанию на самом деле является конкретным методом.

Почему бы не абстрактные/интерфейсные статические методы без реализации по умолчанию? По-видимому, просто из-за того, как Java идентифицирует, какой блок кода он должен выполнить (первая часть моего ответа).

Ответ 14

Объявление метода как static означает, что мы можем вызвать этот метод по имени класса, и если этот класс также является abstract, его не имеет смысла вызывать, так как он не содержит никакого тела, и, следовательно, мы не можем объявить метод как static и abstract.

Ответ 15

Поскольку абстрактный класс является концепцией OOPS, а статические члены не являются частью OOPS....
Теперь мы можем объявить статические методы завершения в интерфейсе и выполнить интерфейс, объявив метод main внутри интерфейса.

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

Ответ 16

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

Таким образом, вы можете создать, например, абстрактный класс orableObject или даже интерфейс с (авто-) абстрактными статическими методами, который определяет параметры параметров сортировки:

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 может еще не иметь элементов, но уже необходимо предоставить предпочтительную сортировку.

Cheerio, Olaf.

Ответ 18

Во-первых, ключевой момент об абстрактных классах - Абстрактный класс не может быть создан (см. wiki). Таким образом, вы не можете создать любой экземпляр абстрактного класса.

Теперь способ java со статическими методами заключается в совместном использовании этого метода со всеми экземплярами этого класса.

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

Boom.

Ответ 19

В соответствии с 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));
    }    
}

Ответ 20

Потому что 'abstract' означает, что метод предназначен для переопределения и нельзя переопределять методы 'static'.

Ответ 21

Регулярные методы могут быть абстрактными, если они предназначены для переопределения подклассами и снабжены функциональностью. Представьте, что класс Foo расширяется на Bar1, Bar2, Bar3 и т.д. Таким образом, каждая из них будет иметь собственную версию абстрактного класса в соответствии с их потребностями.

Теперь статические методы по определению принадлежат классу, они не имеют ничего общего с объектами класса или объектами его подклассов. Им даже не нужно, чтобы они существовали, их можно использовать без создания экземпляров классов. Следовательно, они должны быть готовы к работе и не могут зависеть от подклассов, чтобы добавить к ним функциональность.

Ответ 22

Поскольку абстрактный - это ключевое слово, которое применяется к абстрактным методам, не указывает тело. И если мы говорим о ключевом слове static, то оно относится к области класса.

Ответ 23

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

Ответ 24

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

Ответ 25

Я думаю, вам следует сменить язык и использовать С++, потому что, очевидно, вы не понимаете язык Java. Напишите свою программу на С++, и вы будете свободны, ваша программа будет аварийно завершать работу каждые 30 секунд. Напишите свою программу на Java, которую вам нужно будет отправить. к его парадигмам и ваша программа будет работать долгие месяцы без проблем.