Ответ 1
Потому что "абстрактный" означает: "Не реализует никакой функции", а "статический" означает: "Существует функциональность, даже если у вас нет экземпляра объекта". И это логическое противоречие.
Вопрос в Java, почему я не могу определить абстрактный статический метод? например
abstract class foo {
abstract void bar( ); // <-- this is ok
abstract static void bar2(); //<-- this isn't why?
}
Потому что "абстрактный" означает: "Не реализует никакой функции", а "статический" означает: "Существует функциональность, даже если у вас нет экземпляра объекта". И это логическое противоречие.
Плохой дизайн языка. Было бы гораздо эффективнее напрямую обращаться к статическому абстрактному методу, чем создавать экземпляр только для использования этого абстрактного метода. Особенно верно при использовании абстрактного класса в качестве обходного пути для невозможности расширения перечисления, что является еще одним плохим примером проектирования. Надеюсь, они решают эти ограничения в следующем выпуске.
Вы не можете переопределить статический метод, поэтому сделать его абстрактным будет бессмысленным. Более того, статический метод в абстрактном классе будет принадлежать этому классу, а не первоклассному классу, поэтому его нельзя было использовать в любом случае.
Аннотация метода abstract
для метода указывает, что метод ДОЛЖЕН быть переопределен в подклассе.
В Java член static
(метод или поле) не может быть переопределен подклассами (это не обязательно верно в других объектно-ориентированных языках, см. SmallTalk.) Член static
может быть скрытым, но это принципиально отличается от переопределенного.
Поскольку статические члены не могут быть переопределены в подклассе, аннотация abstract
не может быть применена к ним.
В качестве стороннего - другие языки поддерживают статическое наследование, как и наследование экземпляров. С точки зрения синтаксиса эти языки обычно требуют, чтобы имя класса включалось в оператор. Например, в Java, предполагая, что вы пишете код в ClassA, это эквивалентные утверждения (если методA() является статическим методом, и нет метода экземпляра с одной и той же сигнатурой):
ClassA.methodA();
и
methodA();
В SmallTalk имя класса не является необязательным, поэтому синтаксис (обратите внимание, что SmallTalk не использует., чтобы отделить "объект" и "глагол", но вместо этого использует его как терминатор statemend):
ClassA methodA.
Поскольку имя класса всегда требуется, правильную "версию" метода всегда можно определить, пройдя иерархию классов. Для чего это стоит, я иногда пропускаю наследование static
и укушен отсутствием статического наследования в Java, когда я впервые начал с него. Кроме того, SmallTalk утилит (и, следовательно, не поддерживает программу по контракту). Таким образом, у него нет модификатора abstract
для членов класса.
Я также задал тот же вопрос, вот почему
Поскольку абстрактный класс говорит, он не даст реализации и позволит подклассу дать ему
поэтому Subclass должен переопределить методы Superclass,
ПРАВИЛО № 1 - Статический метод нельзя переопределить
Поскольку статические члены и методы являются элементами времени компиляции, поэтому допустимы перегрузка (полиморфизм времени компиляции) статических методов, а не переопределение (полиморфизм времени выполнения)
Итак, они не могут быть абстрактными.
Нет такой вещи, как абстрактный static < --- Не разрешено в 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();
}
}
}
Используется следующим образом:
static {
Core.setSingleton(new Core.CoreDefaultImpl());
// Or
Core.setSingleton(new Core.CoreTestImpl());
// Later in the application you might use
Core.getRequest();
}
Абстрактный метод определяется только для того, чтобы его можно было переопределить в подклассе. Однако статические методы нельзя переопределить. Таким образом, ошибка времени компиляции имеет абстрактный, статический метод.
Теперь следующий вопрос: почему статические методы нельзя переопределить?
Это потому, что статические методы относятся к определенному классу, а не к его экземпляру. Если вы попытаетесь переопределить статический метод, вы не получите никакой компиляции или ошибки времени выполнения, но компилятор просто скроет статический метод суперкласса.
Статический метод, по определению, не должен знать 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: 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();
}
Предположим, что существуют два класса: 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
действительно не так уж и много.
Абстрактный класс не может иметь статический метод, потому что абстракция выполняется для достижения DYNAMIC BINDING, тогда как статические методы статически привязаны к их функциональности. Статический метод означает поведение не зависит от переменной экземпляра, поэтому нет экземпляра/объекта требуется. Просто класс. Статические методы относятся к классу, а не к объекту. Они хранятся в области памяти, известной как PERMGEN, из которой она делится с каждым объектом. Методы в абстрактном классе динамически привязаны к их функциональности.
Статический метод можно вызывать без экземпляра класса. В вашем примере вы можете вызвать foo.bar2(), но не foo.bar(), потому что для бара вам нужен экземпляр. Следующий код будет работать:
foo var = new ImplementsFoo();
var.bar();
Если вы вызываете статический метод, он будет всегда исполнять тот же код. В приведенном выше примере, даже если вы переопределите bar2 в ImplementsFoo, вызов var.bar2() будет выполнять foo.bar2().
Если bar2 теперь не имеет реализации (что абстрактное означает), вы можете вызвать метод без реализации. Это очень вредно.
Я считаю, что нашел ответ на этот вопрос в форме того, почему методы интерфейса (которые работают как абстрактные методы в родительском классе) не могут быть статичными. Вот полный ответ (не мой)
В основном статические методы могут быть связаны во время компиляции, так как для их вызова вам нужно указать класс. Это отличается от методов экземпляров, для которых класс ссылки, из которой вы вызываете метод, может быть неизвестен во время компиляции (таким образом, какой кодовый блок вызывается, может быть определен только во время выполнения).
Если вы вызываете статический метод, вы уже знаете класс, в котором он реализован, или любые его прямые подклассы. Если вы определяете
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 идентифицирует, какой блок кода он должен выполнить (первая часть моего ответа).
Объявление метода как static
означает, что мы можем вызвать этот метод по имени класса, и если этот класс также является abstract
, его не имеет смысла вызывать, так как он не содержит никакого тела, и, следовательно, мы не можем объявить метод как static
и abstract
.
Поскольку абстрактный класс является концепцией OOPS, а статические члены не являются частью OOPS....
Теперь мы можем объявить статические методы завершения в интерфейсе и выполнить интерфейс, объявив метод main внутри интерфейса.
interface Demo
{
public static void main(String [] args) {
System.out.println("I am from interface");
}
}
Идея иметь абстрактный статический метод заключалась бы в том, что вы не можете использовать этот конкретный абстрактный класс непосредственно для этого метода, но только первая производная будет иметь возможность реализовать этот статический метод (или для дженериков: фактический класс используемый вами общий).
Таким образом, вы можете создать, например, абстрактный класс 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.
Вы можете сделать это с помощью интерфейсов в Java 8.
Это официальная документация:
https://docs.oracle.com/javase/tutorial/java/IandI/defaultmethods.html
Во-первых, ключевой момент об абстрактных классах - Абстрактный класс не может быть создан (см. wiki). Таким образом, вы не можете создать любой экземпляр абстрактного класса.
Теперь способ java со статическими методами заключается в совместном использовании этого метода со всеми экземплярами этого класса.
Итак, если вы не можете создать экземпляр класса, этот класс не может иметь абстрактные статические методы, так как абстрактный метод требует расширения.
Boom.
В соответствии с 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));
}
}
Потому что 'abstract' означает, что метод предназначен для переопределения и нельзя переопределять методы 'static'.
Регулярные методы могут быть абстрактными, если они предназначены для переопределения подклассами и снабжены функциональностью.
Представьте, что класс Foo
расширяется на Bar1, Bar2, Bar3
и т.д. Таким образом, каждая из них будет иметь собственную версию абстрактного класса в соответствии с их потребностями.
Теперь статические методы по определению принадлежат классу, они не имеют ничего общего с объектами класса или объектами его подклассов. Им даже не нужно, чтобы они существовали, их можно использовать без создания экземпляров классов. Следовательно, они должны быть готовы к работе и не могут зависеть от подклассов, чтобы добавить к ним функциональность.
Поскольку абстрактный - это ключевое слово, которое применяется к абстрактным методам, не указывает тело. И если мы говорим о ключевом слове static, то оно относится к области класса.
потому что, если вы используете какой-либо статический член или статическую переменную в классе, он будет загружаться во время загрузки класса.
Потому что, если класс расширяет абстрактный класс, тогда он должен переопределять абстрактные методы, и это обязательно. И поскольку статические методы - это методы класса, разрешенные во время компиляции, тогда как переопределенные методы - это методы экземпляра, разрешенные во время выполнения и после динамического полиморфизма.
Я думаю, вам следует сменить язык и использовать С++, потому что, очевидно, вы не понимаете язык Java. Напишите свою программу на С++, и вы будете свободны, ваша программа будет аварийно завершать работу каждые 30 секунд. Напишите свою программу на Java, которую вам нужно будет отправить. к его парадигмам и ваша программа будет работать долгие месяцы без проблем.