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


150

Ця тема говорить найбільше - що є причиною того, що статичні методи не можуть бути оголошені в інтерфейсі?

public interface ITest {
    public static String test();
}

Код, наведений вище, дає мені таку помилку (принаймні, у Eclipse): "Незаконні модифікатори для методу інтерфейсу ITest.test (); дозволені лише загальнодоступні та абстрактні".


2
Не приймайте відповідь Espo, оскільки вона є хибною. В інтерфейсі є файл класу, який міг би містити реалізацію статичного методу (якщо Java-дизайнер дозволив би це), тому немає проблеми у вирішенні реалізації статичного методу. Він працює точно так само, як і в інших статичних класах.
Мнемент

я начебто згоден з відповіддю на дане «Еріксон» stackoverflow.com/questions/512877 / ...
Maverick

9
Це буде доступно в Java 8 btw.
m0skit0

1
@Vadorequest GIYF, але все одно, перевірте тут
m0skit0

2
Посилання з офіційної документації: Підручник Java SE та специфікація мови Java 9.2
LoganMzz

Відповіді:


85

Тут є кілька проблем. Перший - питання декларування статичного методу без його визначення. Це різниця між

public interface Foo {
  public static int bar();
}

і

public interface Foo {
  public static int bar() {
    ...
  }
}

Перше неможливо з причин, про які згадує Espo : ви не знаєте, який клас реалізації є правильним визначенням.

Java могла дозволити останнє; і насправді, починаючи з Java 8, це і є!


2
Так - це ідеалогічно не технічно. Я б хотів, щоб це було. що в інтерфейсі може бути статичний метод "реалізації", який посилається лише на інші "інтерфейсні" методи в інтерфейсі, які можуть бути легко використані повторно за допомогою класів. Але можна оголосити статичний клас в інтерфейсі, щоб у ньому могли перебувати такі речі, як MyInterface.Impl.doIt (MyInterface i, Object [] args) {...}
peterk

9
Оскільки Java 8, ви можете визначити staticметоди в interface. Методи повинні бути public.
Олів'є Грегоар

4
@ OlivierGrégoire ... і вони не передаються у спадок, що є ключовим.
Вільям Ф. Джеймсон

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

44

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


3
Це пояснення не пояснює проблему. У кожного інтерфейсу є свій клас-файл, він може містити статичний метод. Отже, не потребує пошуку конкретної реалізації.
Мнемент

Не кожен тип інтерфейсу в Java є в його власному файлі, і він не повинен відповідати JLS. Крім того, JLS не передбачає, що класи повинні зберігатися завжди у файловій системі, навпаки.
Влад Гудим

4
@Totophil: Інтерфейси не повинні бути в одному java-файлі, але він матиме власний клас-файл після компіляції. Ось що я написав.
Мнемент

18

Я відповім на ваше запитання прикладом. Припустимо, у нас був клас Math зі статичним методом add. Ви б назвали цей метод так:

Math.add(2, 3);

Якби Math був інтерфейсом замість класу, він не міг би мати жодних визначених функцій. Як таке, говорити щось на зразок Math.add (2, 3) не має сенсу.


11

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

public class A {
   public method x() {...}
}
public class B {
   public method x() {...}
}
public class C extends A, B { ... }

Тепер, що станеться, якщо ви зателефонуєте на Cx ()? Чи буде виконано Ax () або Bx ()? Кожна мова з багаторазовим успадкуванням має вирішити цю проблему.

Інтерфейси дозволяють в Java якось обмежувати багатократне успадкування. Щоб уникнути вищезазначеної проблеми, їм заборонено використовувати методи. Якщо ми розглянемо ту саму проблему з інтерфейсами та статичними методами:

public interface A {
   public static method x() {...}
}
public interface B {
   public static method x() {...}
}
public class C implements A, B { ... }

Тут же проблема, що трапиться, якщо ви зателефонуєте на Cx ()?


Будь-яка причина для голосування? Пояснюючи коментар було б добре.
Менмент

Я не прихильник, але чи не це справедливо і для нестатичних методів?
nawfal

Гаразд, ось дві різні можливості. Метод може бути реалізований або лише оголошений. Я зрозумів, що статичний метод повинен бути реалізований. У цьому сенсі я стикаюся з проблемою, представленою у своїй відповіді. Якщо ви цього не зробите, ви стикаєтесь з описаною проблемою Espo - і цього я не зрозумів, оскільки припускав, що статичний метод буде впроваджений. Ви також не можете оголосити статичний абстрактний метод з цієї причини, спробуйте його, компілятор поскаржиться.
Менмент

Добре, забудьте про частину реалізації. питання в чому, чому ми не можемо заявити. так, компілятор поскаржиться і чому це питання. на який я не думаю, що ти відповів.
nawfal

1
Як би там ситуація буде гірше , ніж мати інтерфейс Aмістять int x(int z);і інтерфейс Bмістять string x(int x);? У чому сенс x(3)інтерфейсу С?
supercat

7

Статичні методи - це не інстанційні методи. Контексту екземпляра немає, тому реалізувати його через інтерфейс мало сенсу.


5

Тепер Java8 дозволяє нам визначати навіть Статичні методи в інтерфейсі.

interface X {
    static void foo() {
       System.out.println("foo");
    }
}

class Y implements X {
    //...
}

public class Z {
   public static void main(String[] args) {
      X.foo();
      // Y.foo(); // won't compile because foo() is a Static Method of X and not Y
   }
}

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


4

Там дуже хороший і лаконічну відповідь на ваше запитання тут . (Мене це вразило як такий добре зрозумілий спосіб пояснення, що я хочу зв'язати це звідси.)


Це не відповідь на питання, у кращому випадку це має бути коментар.
CubeJockey

3

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

interface Foo {
    // ...
    class fn {
        public static void func1(...) {
            // ...
        }
    }
}

Цей самий прийом можна використовувати і в анотаціях:

public @interface Foo {
    String value();

    class fn {
        public static String getValue(Object obj) {
            Foo foo = obj.getClass().getAnnotation(Foo.class);
            return foo == null ? null : foo.value();
        }
    }
}

До внутрішнього класу завжди слід звертатися у формі Interface.fn...замість того Class.fn..., тоді можна позбутися від неоднозначної проблеми.


2

Для поліморфізму використовується інтерфейс, який стосується Об'єктів, а не типів. Тому (як уже зазначалося) стати статичним інтерфейсом немає сенсу.


У деяких рефлексивних контекстах це, здається, має сенс
Cruncher

1

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

public interface StaticMethodInterface {
public static int testStaticMethod() {
    return 0;
}

/**
 * Illegal combination of modifiers for the interface method
 * testStaticMethod; only one of abstract, default, or static permitted
 * 
 * @param i
 * @return
 */
// public static abstract int testStaticMethod(float i);

default int testNonStaticMethod() {
    return 1;
}

/**
 * Without implementation.
 * 
 * @param i
 * @return
 */
int testNonStaticMethod(float i);

}


0

Незаконне поєднання модифікаторів: статичного та абстрактного

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

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

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


Як це відповідає на питання? ОП запитала про інтерфейс, поки ви писали про клас.
Лаки

0

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


статичні методи завжди успадковуються, але їх не можна перекрити.
Акі

0

З Java 8 інтерфейси тепер можуть мати статичні методи.

Наприклад, компаратор має статичний метод naturalOrder ().

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


О БОЖЕ МІЙ! У нас серйозний програміст (і я, коментатор), відповідаючи (і я, коментуючи) 10-річне запитання.
Mohamed Anees

Я не помітив дату :)
Ішара

Ха-ха! Нема проблем!
Mohamed Anees

-2

Можливо, приклад коду допоможе, я буду використовувати C #, але ви повинні мати змогу слідувати далі.

Давайте робити вигляд, що у нас є інтерфейс під назвою IPayable

public interface IPayable
{
    public Pay(double amount);
}

Тепер у нас є два конкретні класи, які реалізують цей інтерфейс:

public class BusinessAccount : IPayable
{
    public void Pay(double amount)
    {
        //Logic
    }
}

public class CustomerAccount : IPayable
{
    public void Pay(double amount)
    {
        //Logic
    }
}

Тепер давайте зробимо вигляд, що у нас є колекція різних облікових записів, для цього ми використаємо загальний список типу IPayable

List<IPayable> accountsToPay = new List<IPayable>();
accountsToPay.add(new CustomerAccount());
accountsToPay.add(new BusinessAccount());

Тепер ми хочемо заплатити $ 50,00 за всі ці рахунки:

foreach (IPayable account in accountsToPay)
{
    account.Pay(50.00);
}

Тож тепер ви бачите, як інтерфейси неймовірно корисні.

Вони використовуються лише на інстанційних об'єктах. Не на статичних класах.

Якби ви зробили статичну оплату, під час переходу через IPayable в accountToPay не було б можливості зрозуміти, чи слід викликати оплату на BusinessAcount або CustomerAccount.


Тільки тому, що статичні методи не мають сенсу в цьому прикладі, це не означає, що вони не мають сенсу в будь-якому прикладі. У вашому прикладі, якщо в інтерфейсі IPayable був статичний метод "IncrementPayables", який відстежував, скільки додано кредиторської заборгованості, це було б справжнім випадком використання. Звичайно, завжди можна використовувати абстрактний клас, але це не те, на що ви зверталися. Ваш приклад сам по собі не підриває статичні методи в інтерфейсах.
Cruncher
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.