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

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

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

30 ответов

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

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

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

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

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

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

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

ClassA.methodA();

а также

methodA();

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

ClassA methodA.

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

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

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

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

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

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

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

Нет такой вещи, как абстрактная статика <--- Не допускается в Java Universe

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

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






}

Статический метод по определению не должен знать this, Таким образом, это не может быть виртуальный метод (который перегружен в соответствии с информацией динамического подкласса, доступной через 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
    }
}

doWork() методы в C1 а также C2 идентичны Может быть много таких calsses: C3C4 и т. д. Если 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();
    }

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

Абстрактный класс не может иметь статический метод, потому что абстракция выполняется для достижения ДИНАМИЧНОЙ СВЯЗИ, в то время как статические методы статически связаны с их функциональностью. Статический метод означает поведение, не зависящее от переменной экземпляра, поэтому экземпляр / объект не требуется. Просто класс. Статические методы принадлежат классу, а не объекту. Они хранятся в области памяти, известной как PERMGEN, откуда она используется для каждого объекта. Методы в абстрактном классе динамически связаны с их функциональностью.

Предположим, есть два класса, 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 метод.

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

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

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

Пример

      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
}

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

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

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

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

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

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

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

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

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

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 определяет, какой блок кода он должен выполнить (первая часть моего ответа).

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

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

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

Привет, Олаф.

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

Потому что "абстрактный" означает, что метод предназначен для переопределения, и нельзя переопределять "статические" методы.

Согласно документу Java:

Статический метод - это метод, связанный с классом, в котором он определен, а не с каким-либо объектом. Каждый экземпляр класса разделяет свои статические методы

В Java 8 наряду с методами по умолчанию в интерфейсе также разрешены статические методы. Это облегчает нам организацию вспомогательных методов в наших библиотеках. Мы можем хранить статические методы, специфичные для интерфейса, в одном интерфейсе, а не в отдельном классе.

Хороший пример этого:

list.sort(ordering);

вместо

Collections.sort(list, ordering);

Другой пример использования статических методов также приведен в самом документе:

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

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

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

В одной строке эта опасная комбинация ( abstract + static ) нарушает объектно-ориентированный принцип полиморфизма.

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

С @Overriding :

Статические методы не поддерживают @overriding (полиморфизм времени выполнения), а только скрытие методов (полиморфизм времени компиляции).

С @скрытием :

Но в ситуации с абстрактными статическими методами родительский (абстрактный) класс не имеет реализации для метода. Следовательно, ссылка на дочерний тип является единственной доступной и не является полиморфизмом .

Дочерняя ссылка является единственной доступной:

По этой причине (подавить функции ООП) язык Java считает абстрактные + статические недопустимой (опасной) комбинацией для методов.

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

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

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

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

Boom.

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

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

позволь мне объяснить :

Абстрактному методу нужен подкласс для его реализации, а затем вы вызываете этот метод с помощью этого экземпляра подкласса. Пример: дочерний элемент наследуется от родителя и реализует один абстрактный метод doSomething(). Теперь вы вызовете этот метод следующим образом:Parent p = new Child(); п.доЧто-то(); здесь вы можете видеть, что вам нужен экземпляр подкласса, чтобы получить его реализацию. Итак, в основном абстрактный метод зависит от экземпляра.

Статический метод принадлежит самому классу. Пример: если родительский класс имеет статический метод doSomething(), вы вызовете этот метод для самого родителя, например Parent.doSomething(); Итак, статический метод не зависит от экземпляра.

Теперь, если вы сделаете статический метод абстрактным, возникнет следующая проблема: Пример: Child1 и Child2 наследуют от Parent и реализуют его статический абстрактный метод doSomething(); при вызове этого метода — Parent.doSomething(); как будет решено, какую реализацию вызывать (Child1 или Child2). Вот почему не допускается использование статического абстрактного метода.

Вы можете сделать это с помощью интерфейсов в Java 8.

Это официальная документация об этом:

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

Другие вопросы по тегам