Питання в 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 не дозволяє статичним методам бути абстрактними. Це означає "дзвонить на заняття". (Це повинно означати "дзвонити тільки для класу", але це інша проблема.) Якщо abstract static
методи, які підтримують Java, я б очікував, що це означає, що метод 1) повинен бути реалізований підкласами, а 2) - метод класу підкласу. Деякі методи просто не мають сенсу як методи екземпляра. На жаль, Java не дозволяє вказати це при створенні абстрактного базового класу (або інтерфейсу).
Поганий дизайн мови. Було б набагато ефективніше викликати статичний абстрактний метод, ніж створювати екземпляр лише для використання цього абстрактного методу. Особливо це стосується використання абстрактного класу як вирішення неможливості розширення enum, що є ще одним поганим прикладом дизайну. Сподіваємось, вони вирішать ці обмеження в наступному випуску.
static
сама по собі є вже порушенням ....
Ви не можете перекрити статичний метод, тому робити його абстрактним було б безглуздо. Більше того, статичний метод в абстрактному класі належав би до цього класу, а не переважаючий клас, тому його не можна було використовувати в будь-якому випадку.
The abstract
Анотації до способу вказує на те, що цей метод повинен бути перевизначений в підкласі.
У Java static
член (метод або поле) не може бути замінений підкласами (це не обов'язково вірно в інших об'єктно-орієнтованих мовах, див. SmallTalk.) static
Член може бути прихованим , але це принципово відрізняється від переосмисленого .
Оскільки статичні члени не можуть бути замінені в підкласі, то abstract
анотація не може бути застосована до них.
Як осторонь - інші мови підтримують статичне успадкування, подібно до спадкування екземплярів. З точки зору синтаксису, ці мови, як правило, вимагають, щоб ім’я класу було включено до виписки. Наприклад, у Java, якщо припустити, що ви пишете код у ClassA, це еквівалентні висловлювання (якщо methodA () є статичним методом, і немає методу екземпляра з однаковою підписом):
ClassA.methodA();
і
methodA();
У SmallTalk назва класу не є обов'язковою, тому синтаксис є (зауважте, що SmallTalk не використовує.
ClassA methodA.
Оскільки назва класу завжди потрібна, правильну "версію" методу завжди можна визначити шляхом проходження ієрархії класів. Що я того вартий, я час від часу пропускаю static
спадщину, і мене вкусила відсутність статичного успадкування на Яві, коли я вперше розпочав його. Крім того, SmallTalk має тип качки (і, отже, не підтримує програму за контрактом.) Таким чином, він не має abstract
модифікаторів для членів класу.
Я теж задав те саме питання, ось чому
Оскільки клас Анотація каже, він не дасть імплементації та дозволить йому надавати підклас
тому підклас повинен перекрити методи суперкласу,
ПРАВИЛО № 1 - Статичний метод не можна перекрити
Оскільки статичні члени та методи є елементами компіляції часу, тому дозволено перевантажувати (Поліморфізм часу компіляції) статичними методами, а не переосмислювати (поліморфізм під час виконання)
Отже, вони не можуть бути абстрактними.
У Всесвіті Java немає такої речі, як абстрактна статична <--- Не дозволено
abstract static
SEE 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
методу, про який ви питаєте у запитанні, і ви написали жирним шрифтом МОЖНА ЗРОБИТИсь в JAVA . Це зовсім неправильно.
Абстрактний метод визначається лише таким чином, щоб його можна було перекрити в підкласі. Однак статичні методи не можна перекрити. Отже, помилка компіляції має час мати абстрактний, статичний метод.
Тепер наступне питання, чому статичні методи не можна перекрити ??
Це тому, що статичні методи належать до певного класу, а не до його примірника. Якщо ви спробуєте перекрити статичний метод, ви не отримаєте жодної помилки компіляції чи виконання, але компілятор просто приховає статичний метод надкласу.
Статичний метод, за визначенням, не повинен знати 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
є ідентичними. Таких випадків може бути багато: 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
методу - це "метод, який оголошується, але не реалізується", це означає, що він сам нічого не повертає.
Визначення a static
методу - це "метод, який повертає те саме значення для тих же параметрів, незалежно від екземпляра, на який він викликається".
An abstract
повертається методу буде змінюватися по мірі зміни примірника. static
Метод не буде. static abstract
Метод в значній мірі спосіб , при якому значення, що повертається є постійним, але не повертає нічого. Це логічне протиріччя.
Також причин для static abstract
методу насправді не так вже й багато .
Абстрактний клас не може мати статичний метод, оскільки абстрагування робиться для досягнення ДИНАМІЧНОГО ОБ'ЄДНАННЯ, а статичні методи статично прив'язані до їх функціональності. Статичний метод означає поведінку, не залежну від змінної екземпляра, тому жоден екземпляр / об'єкт не потрібен. Просто клас. Статичні методи належать до класу, а не до об'єкта. Вони зберігаються в області пам'яті, відомої як PERMGEN, звідки вона спільна з кожним об'єктом. Методи в абстрактному класі динамічно прив’язані до їх функціональності.
Декларуючи метод як static
засіб, ми можемо назвати цей метод за назвою його класу, і якщо цей клас є abstract
, немає сенсу називати його, оскільки він не містить жодного тіла, і, отже, ми не можемо оголосити метод як static
і abstract
.
Оскільки абстрактні методи належать до класу і їх не можна перекрити класом, що реалізує. Навіть якщо є статичний метод з однаковою підписом, він приховує метод, не перекриває його. Тож неважливо оголосити абстрактний метод статичним, оскільки він ніколи не отримає тіло. Отже, складіть помилку часу.
Статичний метод можна викликати без екземпляра класу. У вашому прикладі ви можете зателефонувати 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 визначає, який блок коду повинен виконувати (перша частина моєї відповіді).
Оскільки абстрактний клас - це концепція OOPS, а статичні члени не є частиною OOPS ....
Тепер справа в тому, що ми можемо оголосити статичні повні методи в інтерфейсі, і ми можемо виконати інтерфейс, оголосивши основний метод всередині інтерфейсу.
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 :
Статичний метод - це метод, який асоціюється з класом, у якому він визначений, а не з будь-яким об'єктом. Кожен екземпляр класу поділяє свої статичні методи
У 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));
}
}
Оскільки "абстрактний" означає, що метод має бути замінений, і "статичні" методи не можна замінити.
Регулярні методи можуть бути абстрактними, коли вони мають бути замінені підкласами та забезпечені функціональністю. Уявіть собі класFoo
подовжений і Bar1, Bar2, Bar3
т. Д. Отже, кожен матиме свою версію абстрактного класу відповідно до своїх потреб.
Тепер статичні методи за визначенням належать до класу, вони не мають нічого спільного з об'єктами класу або об'єктами його підкласів. Їм навіть не потрібно, щоб вони існували, вони можуть бути використані без інстанції класів. Отже, вони повинні бути готовими до роботи і не можуть залежати від підкласів, щоб додати їм функціональність.
Оскільки абстракт - це ключове слово, яке застосовується над абстрактними методами, не визначають тіло. А якщо говорити про статичне ключове слово, воно належить до області класу.
Це можна зробити за допомогою інтерфейсів на Java 8.
Це офіційна документація про це:
https://docs.oracle.com/javase/tutorial/java/IandI/defaultmethods.html
Тому що, якщо клас поширює абстрактний клас, то він повинен перекривати абстрактні методи, і це обов'язково. А оскільки статичні методи - це класові методи, вирішені під час компіляції, тоді як перекриті методи - це методи екземпляра, вирішені під час виконання та наступні динамічний поліморфізм.