Вопрос в Java, почему я не могу определить абстрактный статический метод? например
abstract class foo {
abstract void bar( ); // <-- this is ok
abstract static void bar2(); //<-- this isn't why?
}
Вопрос в Java, почему я не могу определить абстрактный статический метод? например
abstract class foo {
abstract void bar( ); // <-- this is ok
abstract static void bar2(); //<-- this isn't why?
}
Ответы:
Потому что «абстрактный» означает: «не реализует никакой функциональности», а «статический» означает: «есть функциональность, даже если у вас нет экземпляра объекта». И это логическое противоречие.
abstract static
это имело бы смысл. Это был бы метод самого объекта класса, который должны реализовывать объекты подкласса. Конечно, как обстоят дела, твой ответ верен, несмотря на мои разговоры о языке.
abstract static
: функция X, которая «реализована в подклассе», не может одновременно «выполняться в классе» - только в подклассе . Где это тогда уже не абстрактно.
static
не означает «не пустой» - это просто следствие того, что Java не позволяет статическим методам быть абстрактными. Это означает «вызываемый в классе». (Это должно означать «вызываемый только в классе», но это другая проблема.) Если бы Java поддерживал abstract static
методы, я ожидал бы, что это означает, что метод 1) должен быть реализован подклассами, а 2) является методом класса подкласса. Некоторые методы просто не имеют смысла как методы экземпляра. К сожалению, Java не позволяет вам указать это при создании абстрактного базового класса (или интерфейса).
Плохой языковой дизайн. Было бы гораздо эффективнее напрямую вызывать статический абстрактный метод, чем создавать экземпляр только для использования этого абстрактного метода. Особенно актуально при использовании абстрактного класса в качестве обходного пути для невозможности расширения перечисления, что является еще одним плохим примером проектирования. Надеюсь, что они решат эти ограничения в следующем выпуске.
static
себя уже является нарушением ....
Вы не можете переопределить статический метод, поэтому делать его абстрактным было бы бессмысленно. Более того, статический метод в абстрактном классе должен принадлежать этому классу, а не переопределяющему классу, поэтому его нельзя использовать в любом случае.
abstract
Аннотации к способу указывает на то, что этот метод должен быть переопределен в подклассе.
В Java static
элемент (метод или поле) не может быть переопределен подклассами (это не обязательно верно в других объектно-ориентированных языках, см. SmallTalk.) Элемент static
может быть скрыт , но это принципиально отличается от переопределенного .
Поскольку статические члены не могут быть переопределены в подклассе, abstract
аннотация к ним не может быть применена.
Кроме того, другие языки поддерживают статическое наследование, как и наследование экземпляров. С точки зрения синтаксиса, эти языки обычно требуют, чтобы имя класса было включено в оператор. Например, в Java, если вы пишете код в ClassA, это эквивалентные операторы (если methodA () является статическим методом, и нет метода экземпляра с такой же сигнатурой):
ClassA.methodA();
а также
methodA();
В SmallTalk имя класса не является обязательным, поэтому синтаксис (обратите внимание, что SmallTalk не использует. Для разделения «субъекта» и «глагола», а вместо этого использует его в качестве терминатора исправления состояния):
ClassA methodA.
Поскольку имя класса всегда требуется, правильная «версия» метода всегда может быть определена путем обхода иерархии классов. Что бы это ни стоило, я иногда пропускаю static
наследование, и меня укусило отсутствие статического наследования в Java, когда я впервые начал с ним. Кроме того, SmallTalk типизирован по типу утки (и поэтому не поддерживает программу по контракту.) Таким образом, он не имеет abstract
модификатора для членов класса.
Я тоже задавал тот же вопрос, вот почему
Так как абстрактный класс говорит, он не даст реализацию и позволит подклассу давать его
поэтому подкласс должен переопределить методы суперкласса,
ПРАВИЛО № 1 - Статический метод не может быть переопределен
Поскольку статические члены и методы являются элементами времени компиляции, поэтому допускается перегрузка (полиморфизм времени компиляции) статических методов, а не переопределение (полиморфизм времени выполнения)
Таким образом, они не могут быть абстрактными.
Нет такой вещи, как абстрактная статика <--- Не допускается в Java Universe
abstract static
см. Stackoverflow.com/questions/370962/… . Реальная причина , почему Java не позволяет статические методы быть переопределены в том , что Java не позволяет статические методы быть переопределены.
foo(String)
не то же самое, foo(Integer)
что и все.
Это ужасный языковой дизайн и на самом деле нет причин, почему это невозможно.
На самом деле, здесь есть реализация того, как это МОЖЕТ быть сделано в 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();
}
abstract static
метода, который был задан в вопросе, и вы написали жирным шрифтом МОЖНО СДЕЛАТЬ В ЯВА . Это полностью вводит в заблуждение.
Абстрактный метод определяется только для того, чтобы его можно было переопределить в подклассе. Однако статические методы не могут быть переопределены. Следовательно, ошибка времени компиляции - иметь абстрактный статический метод.
Теперь следующий вопрос: почему статические методы нельзя переопределить?
Это потому, что статические методы принадлежат определенному классу, а не его экземпляру. Если вы попытаетесь переопределить статический метод, вы не получите никакой ошибки компиляции или времени выполнения, но компилятор просто скроет статический метод суперкласса.
Статический метод по определению не должен знать 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();
}
C1.doWork()
или C2.doWork()
Но вы не можете позвонить C.doWork()
. Также в приведенном вами примере, который не сработает, предположим, если это было разрешено, то как класс C
найдет реализацию doMoreWork()
? Наконец, я бы назвал ваш контекстный код плохим дизайном. Почему? просто потому, что вы создали отдельную функцию для кода, который является уникальным, вместо того, чтобы создать функцию для кода, который является общим, и затем реализовать статическую функцию в классе C
. Это проще !!!
Предположим, есть два класса, 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
метода: «Метод, который возвращает одно и то же значение для тех же параметров независимо от экземпляра, в котором он вызывается».
An abstract
возвращаемого значения метода будет меняться по мере изменения экземпляра. static
Метод не будет. static abstract
Метод в значительной степени способ , при котором возвращаемое значение является постоянным, но не возвращает ничего. Это логическое противоречие.
Кроме того, для static abstract
метода нет особой причины .
Абстрактный класс не может иметь статический метод, потому что абстракция выполняется для достижения ДИНАМИЧНОЙ СВЯЗИ, в то время как статические методы статически связаны с их функциональностью. Статический метод означает поведение, не зависящее от переменной экземпляра, поэтому экземпляр / объект не требуется. Просто класс. Статические методы принадлежат классу, а не объекту. Они хранятся в области памяти, известной как PERMGEN, откуда она используется для каждого объекта. Методы в абстрактном классе динамически связаны с их функциональностью.
Объявление метода как static
означает, что мы можем вызвать этот метод по имени класса, и, если этот класс abstract
также, нет смысла вызывать его, так как он не содержит никакого тела, и, следовательно, мы не можем объявить метод как static
и abstract
.
Поскольку абстрактные методы принадлежат классу и не могут быть переопределены реализующим классом. Даже если есть статический метод с такой же сигнатурой, он скрывает метод, но не переопределяет его. Поэтому несущественно объявлять абстрактный метод статическим, так как он никогда не получит тело. Таким образом, ошибка времени компиляции.
Статический метод может быть вызван без экземпляра класса. В вашем примере вы можете вызвать foo.bar2 (), но не foo.bar (), потому что для 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 определяет, какой блок кода он должен выполнить (первая часть моего ответа).
Поскольку абстрактный класс является концепцией OOPS, а статические члены не являются частью OOPS ....
Теперь мы можем объявить статические методы завершения в интерфейсе и выполнить интерфейс, объявив метод main внутри интерфейса.
interface Demo
{
public static void main(String [] args) {
System.out.println("I am from interface");
}
}
Идея иметь абстрактный статический метод состояла бы в том, что вы не можете использовать этот конкретный абстрактный класс непосредственно для этого метода, но только первая производная будет иметь возможность реализовать этот статический метод (или для обобщений: фактический класс обобщенного вами использование).
Таким образом, вы можете создать, например, абстрактный класс 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 работает со статическими методами, заключается в совместном использовании метода со всеми экземплярами этого класса.
Итак, если вы не можете создать экземпляр класса, этот класс не может иметь абстрактные статические методы, так как абстрактный метод требует расширения.
Boom.
Согласно документу 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
т. Д. Итак, у каждого будет своя версия абстрактного класса в соответствии со своими потребностями.
Теперь статические методы по определению принадлежат классу, они не имеют ничего общего с объектами класса или объектами его подклассов. Им даже не нужно, чтобы они существовали, их можно использовать без создания экземпляров классов. Следовательно, они должны быть готовы к работе и не могут зависеть от подклассов для добавления к ним функциональности.
Потому что абстрактный - это ключевое слово, которое применяется к абстрактным методам, но не определяет тело. И если говорить о статическом ключевом слове, то оно относится к области классов.
Вы можете сделать это с помощью интерфейсов в Java 8.
Это официальная документация об этом:
https://docs.oracle.com/javase/tutorial/java/IandI/defaultmethods.html
Потому что, если класс расширяет абстрактный класс, он должен переопределять абстрактные методы, и это обязательно. А поскольку статические методы являются методами класса, разрешаемыми во время компиляции, тогда как переопределенные методы являются методами экземпляра, разрешенными во время выполнения и после динамического полиморфизма.