Чому я не можу визначити статичний метод в інтерфейсі Java?


498

EDIT: Станом на Java 8, в інтерфейсах тепер дозволені статичні методи.

Ось приклад:

public interface IXMLizable<T>
{
  static T newInstanceFromXML(Element e);
  Element toXMLElement();
}

Звичайно, це не вийде. Але чому б і ні?

Одним із можливих питань може бути те, що відбувається, коли ви телефонуєте:

IXMLizable.newInstanceFromXML(e);

У цьому випадку я думаю, що він повинен просто викликати порожній метод (тобто {}). Усі підкласи будуть змушені реалізовувати статичний метод, тож вони будуть добре під час виклику статичного методу. То чому ж це неможливо?

EDIT: Я думаю, я шукаю відповідь глибша, ніж "тому що так є Java".

Чи є певна технологічна причина, чому статичні методи не можуть бути перезаписані? Тобто, чому дизайнери Java вирішили зробити методи екземпляра перебірними, але не статичними?

EDIT: Проблема в моєму дизайні полягає в тому, що я намагаюся використовувати інтерфейси для забезпечення домовленості про кодування.

Тобто мета інтерфейсу подвійна:

  1. Я хочу, щоб інтерфейс IXMLizable дозволив мені конвертувати класи, які реалізують його, у елементи XML (використання поліморфізму, чудово працює).

  2. Якщо хтось хоче створити новий екземпляр класу, який реалізує інтерфейс IXMLizable, він завжди буде знати, що буде новий статичний конструкторInstanceFromXML (Element e).

Чи є якийсь інший спосіб забезпечити це, окрім просто поміщення коментаря в інтерфейс?


4
Вам не потрібно захаращувати визначення методу (та поля) з public у інтерфейсах, btw.
Том Хотін - тайклін

Гм, здається, дублікат stackoverflow.com/questions/21817/… . Раніше цього не бачив.
Майкл Майєрс

1
Чи можете ви надати якийсь код, як ви хочете використовувати статичні методи інтерфейсу?
Павло Фельдман

43
Це стане можливим на Java 8: docs.oracle.com/javase/tutorial/java/IandI/…
dakshang

1
@dakshang Так, але це не робить те, що хоче ОП.
користувач253751

Відповіді:


518

Java 8 дозволяє статичні методи інтерфейсу

У Java 8 інтерфейси можуть мати статичні методи. Вони також можуть мати конкретні примірники методів, але не поля екземплярів.

Тут справді два питання:

  1. Чому в погані старі часи інтерфейси не могли містити статичні методи?
  2. Чому статичні методи не можна перекрити?

Статичні методи в інтерфейсах

Не було жодної сильної технічної причини, чому інтерфейси не могли мати статичні методи в попередніх версіях. Це красиво підсумовано плакатом дублюючого питання. Статичні методи інтерфейсу спочатку розглядалися як невелика зміна мови, а потім була офіційна пропозиція додати їх у Java 7, але пізніше її відмовили через непередбачені ускладнення.

Нарешті, Java 8 представила методи статичного інтерфейсу, а також способи переопределення екземплярів із реалізацією за замовчуванням. Вони все ще не можуть мати поля примірників. Ці функції є частиною підтримки лямбда-експресії, і ви можете прочитати більше про них у частині H JSR 335.

Переважаючі статичні методи

Відповідь на друге питання дещо складніша.

Статичні методи вирішуються під час компіляції. Динамічна диспетчеризація має сенс для, наприклад, методів, коли компілятор не може визначити конкретний тип об'єкта, і, таким чином, не може вирішити метод для виклику. Але для виклику статичного методу потрібен клас, а оскільки цей клас статично відомий - у час компіляції - динамічна відправка непотрібна.

Невелика інформація про те, як працюють методи екземплярів, необхідна, щоб зрозуміти, що тут відбувається. Я впевнений, що реальна реалізація зовсім інша, але дозвольте мені пояснити моє поняття відправлення методу, в яких моделях точно спостерігалася поведінка.

Притворіть, що для кожного класу є хеш-таблиця, яка відображає підписи методів (типи імен та параметрів) у фактичний фрагмент коду для реалізації методу. Коли віртуальна машина намагається викликати метод на екземпляр, він запитує об'єкт для свого класу та шукає запитуваний підпис у таблиці класу. Якщо знайдено тіло методу, воно викликається. В іншому випадку отримується батьківський клас класу, і пошук там повторюється. Це триває до тих пір, поки метод не буде знайдений або не існує більше батьківських класів, що призводить до появи a NoSuchMethodError.

Якщо обидва надкласи та підкласи мають записи у своїх таблицях для одного і того ж підпису методу, спочатку зустрічається версія підкласу, а версія суперкласу ніколи не використовується - це "перевизначення".

Тепер, припустимо, ми пропускаємо екземпляр об'єкта і просто починаємо з підкласу. Роздільна здатність може продовжуватися так, як зазначено вище, надаючи свого роду "перезаписний" статичний метод. Роздільна здатність може статися в час компіляції, оскільки компілятор починає з відомого класу, а не чекає, поки час виконання буде запитувати об'єкт не визначеного типу для його класу. Немає сенсу "переосмислювати" статичний метод, оскільки завжди можна вказати клас, який містить бажану версію.


"Інтерфейси" конструктора

Ось трохи більше матеріалу для вирішення цього питання.

Це здається, що ви хочете ефективно призначити конструктор-подібний метод для кожної реалізації IXMLizable. Забудьте про те, щоб спробувати застосувати це за допомогою інтерфейсу на хвилину, і зробіть вигляд, що у вас є кілька класів, які відповідають цій вимозі. Як би ти ним користувався?

class Foo implements IXMLizable<Foo> {
  public static Foo newInstanceFromXML(Element e) { ... }
}

Foo obj = Foo.newInstanceFromXML(e);

Оскільки вам потрібно чітко назвати конкретний тип Fooпри "конструюванні" нового об'єкта, компілятор може перевірити, чи справді він має необхідний заводський метод. А якщо це не так, то що? Якщо я можу реалізувати , IXMLizableщо не вистачає «конструктора», і я створюю примірник і передати його в код, він єIXMLizable всім необхідним інтерфейсом.

Побудова є частиною реалізації, а не інтерфейсом. Будь-який код, який успішно працює з інтерфейсом, не хвилює конструктор. Будь-який код, який піклується про конструктор, все одно повинен знати конкретний тип, і інтерфейс можна ігнорувати.


Гарний дзвінок на проектну монету. mail.openjdk.java.net/pipermail/coin-dev/2009-March/000117.html
Майкл Майєрс

12
Чи може причина №1 бути багаторазовим успадкуванням? Оскільки ми можемо успадкувати з декількох інтерфейсів, якщо два інтерфейси містили один і той же статичний метод підпису, а потім клас реалізував їх обидва і називав цей метод, то все може ускладнитися таким чином, чого творці мови Java хотіли уникнути, заборонивши множинне успадкування класів у перше місце. Очевидно, такий же аргумент можна зробити і для інтерфейсу, який взагалі не дозволяє визначити будь-який метод.
shrini1000

1
@ shrini1000 - Ні, статичні методи вирішуються під час компіляції. Неоднозначність може бути оброблена так само, як і з константами: з помилкою компілятора. Однак пропозицію в рамках проектної монети було відхилено, посилаючись на деякі непередбачені труднощі. Не впевнений, що це було, але я не думаю, що так було.
erickson

1
@ tgm1024 Так, у розділі "Інтерфейси конструктора" пояснюється, чому не має сенсу намагатися викликати поліморфну ​​поведінку через тип, відомий під час компіляції. Як би ви посилалися RESET()на даний клас? Ви б написали SomeClass.RESET(). Тому вам не потрібен інтерфейс для опису цього API; його статичність. Інтерфейси використовуються, коли ви не знаєте конкретного типу під час компіляції. Це ніколи не буває зі статичним методом.
erickson

2
"Конструкція є частиною реалізації, а не інтерфейсом. Будь-який код, який успішно працює з інтерфейсом, не стосується конструктора." - Це явно не вірно. В інших мовах (наприклад, Swift) я можу створювати нові екземпляри, Tне знаючи Tстатично, тому що я обіцяю в інтерфейсі, що певний конструктор (або статичний метод) буде існувати під час виконання. Те, що в Java неможливо вказати покоління, не означає, що це не має сенсу робити.
Рафаель

48

Про це вже запитали і відповіли, ось

Щоб дублювати мою відповідь:

Ніколи не має сенсу оголошувати статичний метод в інтерфейсі. Вони не можуть бути виконані звичайним викликом MyInterface.staticMethod (). Якщо ви викликаєте їх, вказавши клас реалізації MyImplementor.staticMethod (), ви повинні знати фактичний клас, тому не має значення, чи містить його чи ні інтерфейс.

Що ще важливіше, статичні методи ніколи не змінюються, і якщо ви намагаєтесь це зробити:

MyInterface var = new MyImplementingClass();
var.staticMethod();

правила статики кажуть, що метод, визначений у оголошеному типі var, повинен бути виконаний. Оскільки це інтерфейс, це неможливо.

Причина, по якій ви не можете виконати "result = MyInterface.staticMethod ()", полягає в тому, що йому доведеться виконати версію методу, визначеного в MyInterface. Але в MyInterface не може бути визначена версія, оскільки це інтерфейс. Він не має коду за визначенням.

Хоча ви можете сказати, що це означає "тому, що Java робить це так", насправді рішення є логічним наслідком інших дизайнерських рішень, також прийнятих з дуже вагомих причин.


14
Якщо ви використовуєте <T розширює MyInterface> як параметр загального типу, було б непогано гарантувати через інтерфейс, що T може .doSomething ().
Кріс Бетті

4
Хоча я розумію аргументи, я погоджуюся з @Chris_Betti (навіть для негенеричних типів): було б непогано, щоб структура коду гарантувала, що деякі класи реалізують певний статичний API. Можливо, це можливо, використовуючи іншу концепцію ...
Juh_

@Juh_, ..... або нове ключове слово, якщо це абсолютно потрібно. Я вважаю, що staticце все одно кричущий термін у мовах, і це було занадто далеко. Тож мати його само по собі вже схематично. Дивіться мій приклад вище stackoverflow.com/questions/512877/… {shrug}.

3
Це здається неправдивим: "Ніколи не має сенсу оголошувати статичний метод в інтерфейсі". Якщо у мене є колекція класів, які, не будучи примірниками, могли б запропонувати мені деяку інформацію, але мені знадобиться спільний інтерфейс, щоб помістити цю статичну інформацію про рівень класу (тобто інтерфейс із статичним методом, що перезаписується), то це дійсне використання . Подумайте про рефлексію ++, де ви могли б зафіксувати метаінформацію про властивості класу, не збиваючись з атрибутами, відображенням тощо
Jason

1
"Ніколи не має сенсу оголошувати статичний метод в інтерфейсі." Це неправда: уявіть, що у вашій системі є роздільна здатність класу за замовчуванням. Якщо він виявить, що ви реалізуєте метод ContainerInjectionInterce :: create (Container $ контейнер), він, наприклад, створить об'єкт з цією функцією.
користувач3790897

37

Зазвичай це робиться за допомогою заводської схеми

public interface IXMLizableFactory<T extends IXMLizable> {
  public T newInstanceFromXML(Element e);
}

public interface IXMLizable {
  public Element toXMLElement();
}

7
+1 фабричний візерунок звучить як рішення проблеми. (правда, не до питання)
pvgoddijn

Хтось може сказати мені, який сенс розміщувати тут <T розширює IXMLizable>. Я новачок у Java. Що це робить?
Нуван Харшакумара Піяратна

1
@NuwanHarshakumaraPiyarathna T повинен бути класом, що розширює IXMLizable. Подивіться на дженерики Java, щоб краще зрозуміти, що це означає
Адріан

37

З появою Java 8 тепер можна записати за замовчуванням і статичні методи в інтерфейс. docs.oracle/staticMethod

Наприклад:

public interface Arithmetic {

    public int add(int a, int b);

    public static int multiply(int a, int b) {
        return a * b;
    }
}
public class ArithmaticImplementation implements Arithmetic {

    @Override
    public int add(int a, int b) {
        return a + b;
    }

    public static void main(String[] args) {
        int result = Arithmetic.multiply(2, 3);
        System.out.println(result);
    }
}

Результат : 6

ПОРАДА: Виклик статичного методу інтерфейсу не потребує реалізації будь-якого класу. Звичайно, це відбувається тому, що ті ж правила для статичних методів у суперкласах застосовуються і для статичних методів на інтерфейсах.


Це прекрасний приклад цього питання.

21

Оскільки статичні методи не можуть бути замінені в підкласах, а значить, вони не можуть бути абстрактними. І всі методи в інтерфейсі фактично абстрактні.


2
Ви завжди можете змусити кожен тип реалізувати будь-які методи статичного інтерфейсу. Класові класи, хтось?
MichaelGG

16
Вийдіть поза себе і дайте відповідь на запитання: Чому статичні методи не можна перекрити? Якби статичні методи могли бути відмінені, як це виглядатиме? Що ви могли з ними зробити? Ця відповідь в основному "Ви не можете, тому що не можете".
erickson

10

Чому я не можу визначити статичний метод в інтерфейсі Java?

Насправді можна на Java 8.

Відповідно до документа 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));
    }    
}

6

Інтерфейси стосуються поліморфізму, який притаманний примірникам об'єктів, а не класам. Тому статичність не має сенсу в контексті інтерфейсу.


Чітка, лаконічна логіка. Добре кажучи.
Оке Увечуе

6

По-перше, усі мовні рішення - це рішення, які приймають мовні творці. Немає нічого у світі програмної інженерії чи мови, що визначає або пише компілятор / інтерпретатор, що говорить, що статичний метод не може бути частиною інтерфейсу. Я створив для них пару мов та написаних компіляторів - це все просто сідаючи та визначаючи змістовну семантику. Я б стверджував, що семантика статичного методу в інтерфейсі надзвичайно зрозуміла - навіть якщо компілятор повинен відкласти роздільну здатність методу на час виконання.

По-друге, те, що ми взагалі використовуємо статичні методи, є поважна причина наявності інтерфейсу, який включає статичні методи - я не можу говорити ні для кого з вас, але я регулярно використовую статичні методи.

Найімовірнішою правильною відповіддю є те, що не було сприйнятої потреби в статичних методах в інтерфейсах під час визначення мови. Java протягом багатьох років сильно зросла, і це предмет, який, очевидно, викликав певний інтерес. Те, що його шукали для Java 7, вказує на те, що її підвищився до рівня інтересу, який може призвести до зміни мови. Я, наприклад, буду радий, коли мені більше не доведеться інстанціювати об'єкт, просто я можу викликати свій нестатичний метод getter для доступу до статичної змінної в екземплярі підкласу ...


5

Статичні методи не є віртуальними, як методи екземплярів, тому я думаю, що дизайнери Java вирішили, що вони не хочуть їх в інтерфейсах.

Але ви можете розміщувати класи, що містять статичні методи всередині інтерфейсів. Ви можете спробувати це!

public interface Test {
    static class Inner {
        public static Object get() {
            return 0;
        }
    }
}

5
  • "Чи є певна причина, що статичні методи не можуть бути переопрацьовані".

Дозвольте мені переписати це питання для вашого, заповнивши визначення.

  • "Чи є певна причина, що методи, вирішені під час компіляції, не можуть бути вирішені під час виконання."

Або, якщо сказати більше, якщо я хочу викликати метод без екземпляра, але знаючи клас, як я можу його вирішити на основі екземпляра, який у мене немає.


3

У кількох відповідях обговорювалися проблеми з концепцією перезаписуваних статичних методів. Однак іноді ви стикаєтесь з такою схемою, де здається, що це саме те, що ви хочете використовувати.

Наприклад, я працюю з об'єктно-реляційним шаром, який має цінні об'єкти, але також має команди для управління об'єктами цінності. З різних причин кожен клас об'єктного значення повинен визначати деякі статичні методи, які дозволяють фреймворку знайти екземпляр команди. Наприклад, щоб створити особу, яку ви робите:

cmd = createCmd(Person.getCreateCmdId());
Person p = cmd.execute();

і завантажити особу за ідентифікатором, який ви робили

cmd = createCmd(Person.getGetCmdId());
cmd.set(ID, id);
Person p = cmd.execute();

Це досить зручно, проте у нього є свої проблеми; особливо існування статичних методів не може бути застосовано в інтерфейсі. Перезабір статичного методу в інтерфейсі був би саме тим, що нам потрібно, якби він міг якось працювати.

EJB вирішують цю проблему за допомогою домашнього інтерфейсу; кожен об'єкт знає, як знайти свій дім, а дім містить "статичні" методи. Таким чином, "статичні" методи можуть бути замінені в міру необхідності, і ви не захаращуєте звичайний (це називається "Віддалений") інтерфейс із методами, які не застосовуються до екземпляра вашого біна. Просто змусьте звичайний інтерфейс вказати метод "getHome ()". Поверніть екземпляр домашнього об’єкта (який, можливо, може бути однотонним), і абонент може виконувати операції, що впливають на всі об'єкти Person.


3

Коментуючи EDIT: As of Java 8, static methods are now allowed in interfaces.

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


2

Ну і без дженерики статичні інтерфейси марні, оскільки всі виклики статичних методів вирішуються під час компіляції. Отже, реальної користі для них немає.

З дженериками вони використовують - із реалізацією за замовчуванням або без неї. Очевидно, що там потрібно було б переважати тощо. Однак, я гадаю, що таке використання було не дуже OO (як інші відповіді тупо вказують), а отже, не вважалося вартим зусиль, які вони вимагатимуть для корисної реалізації.


1
Які стосунки до цього мають дженерики? Статичний метод на інтерфейсі все ще буде невиконаним.
DJClayworth

По-перше, це було б рішення про впровадження. Але я здогадуюсь, що він не хоче викликати статичні методи в інтерфейсі (він може просто використовувати клас). Але натомість хоче мати щось різновид, наприклад тип класу чи що не над параметрами типу. Насправді його остання редакція показує це ще виразніше.
MichaelGG

2
Why can't I define a static method in a Java interface?

Усі методи в інтерфейсі явно абстрактні, і тому ви не можете визначити їх як статичні, оскільки статичні методи не можуть бути абстрактними.


1

Інтерфейс ніколи не може бути разименовиваются статично, наприклад ISomething.member. Інтерфейс завжди відмінюється через змінну, яка посилається на екземпляр підкласу інтерфейсу. Таким чином, посилання на інтерфейс ніколи не може знати, до якого підкласу він посилається без примірника свого підкласу.

Таким чином, найближчим наближенням до статичного методу в інтерфейсі було б нестатичний метод, який ігнорує "це", тобто не отримує доступу до будь-яких нестатичних членів екземпляра. У абстракції низького рівня кожен нестатичний метод (після пошуку в будь-якому vtable) насправді є лише функцією з областю класу, яка приймає "це" як неявний формальний параметр. Дивіться однотонний об'єкт Scala та сумісність з Java як доказ цієї концепції. Таким чином, кожен статичний метод є функцією з областю класу, яка не приймає параметр "цей". Таким чином, статичний метод зазвичай можна назвати статичним, але, як було сказано раніше, інтерфейс не має реалізації (є абстрактним).

Таким чином, щоб отримати найближче наближення до статичного методу в інтерфейсі, це використовувати нестатичний метод, а потім не отримувати доступу до жодного з членів нестатичного екземпляра. Не було б можливої ​​користі від продуктивності будь-якого іншого способу, оскільки немає способу статично зв’язати (під час компіляції) a ISomething.member(). Єдина перевага, яку я бачу в статичному методі в інтерфейсі, полягає в тому, що він не вводить (тобто ігнорує) неявне "це" і, таким чином, забороняє доступ до будь-якого з нестатичних членів екземпляра. Це зазначає неявно, що функція, яка не отримує доступу до "цього", є мутаційною і навіть не читається лише щодо класу, що містить її. Але оголошення "статичного" в інтерфейсі ISomethingтакож бентежить людей, які намагалися отримати доступ до ньогоISomething.member()що призведе до помилки компілятора. Я вважаю, що якщо помилка компілятора була достатньо пояснювальною, було б краще, ніж намагатися навчити людей використовувати нестатичний метод для досягнення того, чого вони хочуть (мабуть, переважно фабричні методи), як ми це робимо тут (і це повторювалося протягом 3 Питання та відповіді на цьому веб-сайті), тому це, очевидно, проблема, яка не є інтуїтивно зрозумілою для багатьох людей. Мені довелося подумати над цим деякий час, щоб отримати правильне розуміння.

Спосіб отримання змінного статичного поля в інтерфейсі - це використання нестатичних методів отримання та встановлення в інтерфейсі, щоб отримати доступ до цього статичного поля, що знаходиться в підкласі. Сторонні, очевидно незмінні статики можуть бути оголошені в інтерфейсі Java з static final.


0

Інтерфейси просто містять перелік речей, які надасть клас, а не фактичну реалізацію цих речей, що є вашим статичним елементом.

Якщо ви хочете статики, використовуйте абстрактний клас і успадкуйте його, інакше видаліть статику.

Сподіваюся, що це допомагає!


2
Ну, теоретично, ви могли б визначити інтерфейс для включення статичної поведінки, тобто "реалізації цього інтерфейсу матимуть статичний метод foo () з цим підписом" і залишати реалізацію до конкретного класу. Я стикався з ситуаціями, коли така поведінка була б корисною.
Роб

0

Ви не можете визначити статичні методи в інтерфейсі, оскільки статичні методи належать до класу, а не до екземпляра класу, а інтерфейси - не Класи. Детальніше читайте тут.

Однак якщо ви хочете, можете зробити це:

public class A {
  public static void methodX() {
  }
}

public class B extends A {
  public static void methodX() {
  }
}

У цьому випадку у вас є два класи з двома різними статичними методами під назвою methodX ().


0

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

interface Iface {
  public static void thisIsTheMethod();
}

class A implements Iface {

  public static void thisIsTheMethod(){
    system.out.print("I'm class A");
  }

}

class B extends Class A {

  public static void thisIsTheMethod(){
    System.out.print("I'm class B");
  } 
}

SomeClass {

  void doStuff(Iface face) {
    IFace.thisIsTheMethod();
    // now what would/could/should happen here.
  }

}

1
На ній було б надруковано "Я клас А". Однак, якщо ви введете текст, A.thisIsTheMethod()він надрукував би "Я клас Б".
cdmckay

але ви називаєте методи на інтерфейсі, як би ви (або компілятор) знали, який метод слід викликати? (Rember може бути більше класів, котрі дрикетно реалізують Iface
pvgoddijn

Вибачте, я мав на увазі сказати: Однак, якщо ви введете B.thisIsTheMethod()його, було б надруковано "Я клас B".
cdmckay

Я спеціально сказав IFace.thisIsTHeMethod, оскільки в цьому криється проблема. не можна було б викликати його по інтерфейсу без невизначеної поведінки (навіть незважаючи на це на ньому)
pvgoddijn

0

Щось, що може бути реалізовано, - це статичний інтерфейс (замість статичного методу в інтерфейсі). Усі класи, що реалізують даний статичний інтерфейс, повинні реалізовувати відповідні статичні методи. Ви можете отримати статичний інтерфейс SI з будь-якого класу, використовуючи клазз

SI si = clazz.getStatic(SI.class); // null if clazz doesn't implement SI
// alternatively if the class is known at compile time
SI si = Someclass.static.SI; // either compiler errror or not null

тоді ви можете зателефонувати si.method(params). Це було б корисно (наприклад, для дизайну заводських моделей), оскільки ви можете отримати (або перевірити реалізацію) впровадження статичних методів SI з невідомого за часом компіляції класу! Необхідна динамічна відправка, і ви можете перекрити статичні методи (якщо не остаточні) класу, розширивши його (коли викликається через статичний інтерфейс). Очевидно, що ці методи можуть отримати доступ лише до статичних змінних свого класу.


0

Хоча я усвідомлюю, що Java 8 вирішує цю проблему, я подумав, що я заздалегідь прислухаюся до сценарію, над яким я зараз працюю (заблокований із використанням Java 7), де було б корисно вказувати статичні методи в інтерфейсі.

У мене є кілька визначень перерахунків, де я визначав поля "id" та "displayName" разом із допоміжними методами оцінки значень з різних причин. Реалізація інтерфейсу дозволяє мені переконатися, що методи геттера є на місці, але не статичні методи помічників. Будучи перерахунком, насправді не існує чистого способу перевантажити хелперні методи в успадкований абстрактний клас чи щось подібне, тому методи повинні визначатися в самій перерахунку. Крім того, що це перерахунок, ви ніколи не зможете насправді передавати його як об'єкт, що вбудовується, і трактувати його як тип інтерфейсу, але, будучи в змозі вимагати існування статичних допоміжних методів через інтерфейс, це те, що мені подобається його підтримують у Java 8.

Ось код, який ілюструє мою думку.

Визначення інтерфейсу:

public interface IGenericEnum <T extends Enum<T>> {
    String getId();
    String getDisplayName();
    //If I was using Java 8 static helper methods would go here
}

Приклад визначення одного перерахунку:

public enum ExecutionModeType implements IGenericEnum<ExecutionModeType> {
    STANDARD ("Standard", "Standard Mode"),
    DEBUG ("Debug", "Debug Mode");

    String id;
    String displayName;

    //Getter methods
    public String getId() {
        return id;
    }

    public String getDisplayName() {
        return displayName;
    }

    //Constructor
    private ExecutionModeType(String id, String displayName) {
        this.id = id;
        this.displayName = displayName;
    }

    //Helper methods - not enforced by Interface
    public static boolean isValidId(String id) {
        return GenericEnumUtility.isValidId(ExecutionModeType.class, id);
    }

    public static String printIdOptions(String delimiter){
        return GenericEnumUtility.printIdOptions(ExecutionModeType.class, delimiter);
    }

    public static String[] getIdArray(){
        return GenericEnumUtility.getIdArray(ExecutionModeType.class);
    }

    public static ExecutionModeType getById(String id) throws NoSuchObjectException {
        return GenericEnumUtility.getById(ExecutionModeType.class, id);
    }
}

Визначення загальної утиліти enum:

public class GenericEnumUtility {
    public static <T extends Enum<T> & IGenericEnum<T>> boolean isValidId(Class<T> enumType, String id) {       
        for(IGenericEnum<T> enumOption : enumType.getEnumConstants()) {
            if(enumOption.getId().equals(id)) {
                return true;
            }
        }

        return false;
    }

    public static <T extends Enum<T> & IGenericEnum<T>> String printIdOptions(Class<T> enumType, String delimiter){
        String ret = "";
        delimiter = delimiter == null ? " " : delimiter;

        int i = 0;
        for(IGenericEnum<T> enumOption : enumType.getEnumConstants()) {
            if(i == 0) {
                ret = enumOption.getId();
            } else {
                ret += delimiter + enumOption.getId();
            }           
            i++;
        }

        return ret;
    }

    public static <T extends Enum<T> & IGenericEnum<T>> String[] getIdArray(Class<T> enumType){
        List<String> idValues = new ArrayList<String>();

        for(IGenericEnum<T> enumOption : enumType.getEnumConstants()) {
            idValues.add(enumOption.getId());
        }

        return idValues.toArray(new String[idValues.size()]);
    }

    @SuppressWarnings("unchecked")
    public static <T extends Enum<T> & IGenericEnum<T>> T getById(Class<T> enumType, String id) throws NoSuchObjectException {
        id = id == null ? "" : id;
        for(IGenericEnum<T> enumOption : enumType.getEnumConstants()) {
            if(id.equals(enumOption.getId())) {
                return (T)enumOption;
            }
        }

        throw new NoSuchObjectException(String.format("ERROR: \"%s\" is not a valid ID. Valid IDs are: %s.", id, printIdOptions(enumType, " , ")));
    }
}

0

Припустимо, статичні методи були дозволені в інтерфейсах: * Вони змусять усі класи, що реалізують, оголосити цей метод. * Інтерфейси зазвичай використовуються через об'єкти, тому єдиними ефективними методами для них будуть нестатичні. * Будь-який клас, який знає певний інтерфейс, може використовувати його статичні методи. Отже, статичний метод класу реалізації буде названий під ним, але клас виклику не знає, який. Як це знати? У нього немає жодного приводу здогадуватися про це!

Вважалося, що інтерфейси використовуються при роботі з об'єктами. Таким чином, об'єкт виділяється з конкретного класу, тому ця остання справа вирішена. Класу, що викликає, не потрібно знати, який саме клас є, тому що інстанціювання може бути зроблено третім класом. Тож клас виклику знає лише інтерфейс.

Якщо ми хочемо, щоб це було розповсюджено на статичні методи, ми повинні мати можливість конкретизувати клас реалізації, а потім передати посилання на клас виклику. Це могло б використовувати клас через статичні методи в інтерфейсі. Але чим відрізняється ця довідка від об'єкта? Нам просто потрібен об’єкт, що представляє, яким він був клас. Тепер об'єкт представляє старий клас і міг реалізувати новий інтерфейс, включаючи старі статичні методи - ті, які тепер нестатичні.

Для цього служать метакласи. Ви можете спробувати клас Клас Java. Але проблема в тому, що Java недостатньо гнучка для цього. Не можна оголосити метод в об’єкті класу інтерфейсу.

Це мета-питання - коли вам потрібно зробити дупу

..бла бла

у будь-якому випадку у вас легко вирішити - зробити метод нестатичним з тією ж логікою. Але тоді вам доведеться спочатку створити об’єкт для виклику методу.


0

Для вирішення цього питання: помилка: відсутнє тіло методу або оголосити абстрактний статичний недійсний основний (аргумент String []);

interface I
{
    int x=20;
    void getValue();
    static void main(String[] args){};//Put curly braces 
}
class InterDemo implements I
{
    public void getValue()
    {
    System.out.println(x);
    }
    public static void main(String[] args)
    {
    InterDemo i=new InterDemo();
    i.getValue();   
    }

}

вихід: 20

Тепер ми можемо використовувати статичний метод в інтерфейсі


1
Це марно, хоча. Визначення статичного методу в інтерфейсі не примушує подальших визначень у класах, які реалізують такий інтерфейс. Якщо ви просто видалите статичний метод з інтерфейсу I, ваш код все одно складеться і запуститься без проблем. Іншими словами, ви НЕ перекриваєте основний метод інтерфейсу в класі InterDemo, просто створюєте новий метод з тією ж підписом.
Fran Marzoa

-2

Я думаю, що у Java немає статичних методів інтерфейсу, оскільки вони вам не потрібні. Ви можете подумати, що ви робите, але ... Як би ви ними користувалися? Якщо ви хочете називати їх так

MyImplClass.myMethod()

тоді вам не потрібно декларувати це в інтерфейсі. Якщо ви хочете називати їх так

myInstance.myMethod()

то воно не повинно бути статичним. Якщо ви насправді збираєтесь скористатися першим способом, але просто хочете примусити кожну реалізацію мати такий статичний метод, то це дійсно умова кодування, а не контракт між екземпляром, який реалізує інтерфейс та код виклику.

Інтерфейси дозволяють визначати контракт між екземпляром класу, який реалізує інтерфейс, та кодом виклику. І java допомагає вам бути впевненим, що цей контракт не порушений, тому ви можете покластися на нього і не хвилюватися, який клас реалізує цей контракт, достатньо лише "того, хто підписав контракт". У разі статичних інтерфейсів ваш код

MyImplClass.myMethod()

не покладається на той факт, що кожна реалізація інтерфейсу має цей метод, тому вам не потрібно java, щоб допомогти вам бути впевненим у цьому.


-5

Що потребує статичного методу в інтерфейсі, статичні методи використовуються в основному, коли вам не потрібно створювати екземпляр об'єкта, ціла ідея інтерфейсу - це введення концепцій OOP із введенням статичного методу, який ви відволікаєте від концепції.

Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.