Почему я не могу объявить статические методы в интерфейсе?

150

Тема говорит о большинстве из этого - что является причиной того, что статические методы не могут быть объявлены в интерфейсе?

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

Приведенный выше код дает мне следующую ошибку (по крайней мере, в Eclipse): «Недопустимый модификатор для метода интерфейса ITest.test (); разрешены только public и abstract».

Хенрик Пол
источник
2
Пожалуйста, не принимайте ответ Эспо, так как он ошибочен. Интерфейс имеет файл класса, который может содержать реализацию статического метода (если Java-дизайнер позволил бы это), поэтому нет проблем с разрешением реализации статического метода. Он работает точно так же, как и с другими статическими классами.
Мнемент
Я вроде согласен с ответом, который дал "erickson" stackoverflow.com/questions/512877/…
Maverick
9
Это будет доступно в Java 8, кстати.
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() {
    ...
  }
}

Первое невозможно по причинам, которые упоминает Эспо : вы не знаете, какой класс реализации является правильным определением.

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 не будет беспокоиться о поиске экземпляра класса при попытке выполнить статический метод. Это связано с тем, что статические методы не зависят от экземпляра и, следовательно, могут выполняться прямо из файла класса. Учитывая, что все методы в интерфейсе являются абстрактными, ВМ должна была бы искать конкретную реализацию интерфейса, чтобы найти код, стоящий за статическим методом, чтобы он мог быть выполнен. Это противоречит принципу разрешения статического метода и может привести к несогласованности в языке.

Эспоо
источник
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 ()?

Mnementh
источник
Есть ли причина для понижения? Объясняющий комментарий был бы хорош.
Мнемент
я не downvoter, но разве это не подходит для нестатических методов тоже?
Nawfal
ОК, здесь есть две разные возможности. Метод может быть реализован или только объявлен. Я понял, что должен быть реализован статический метод. В этом смысле я сталкиваюсь с проблемой, представленной в моем ответе. Если вы этого не сделаете, вы столкнетесь с проблемой, описанной Эспо, - и я не понял, потому что предполагал, что статический метод будет реализован. Вы также не можете объявить статический абстрактный метод по этой причине, попробуйте его, компилятор будет жаловаться.
Мнемент
Хорошо, забудьте о реализации. вопрос в том, почему мы не можем объявить. да, компилятор будет жаловаться, и почему это вопрос. на что я не думаю, что вы ответили.
Nawfal
1
Как бы там ситуация будет хуже , чем иметь интерфейс Aсодержат int x(int z);и интерфейс Bсодержат string x(int x);? Что означает x(3)в интерфейсе C?
Суперкат
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
   }
}

Примечание: Методы в Интерфейсе по-прежнему являются публичными абстрактными по умолчанию, если мы явно не используем ключевые слова default / static, чтобы сделать их Методами по умолчанию и Статическими методами, соответственно.

Anandaraja_Srinivasan
источник
4

Там очень хороший и лаконичный ответ на ваш вопрос здесь . (Это показалось мне настолько простым и понятным способом объяснить это, что я хочу связать это отсюда.)

Zarkonnen
источник
Это не ответ на вопрос, в лучшем случае это должен быть комментарий.
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..., чтобы избавиться от неоднозначной проблемы.

Xiè Jìléi
источник
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

Недопустимая комбинация модификаторов: статические и абстрактные

Если член класса объявлен как статический, он может использоваться с именем класса, которое ограничено этим классом, без создания объекта.

Если член класса объявлен как абстрактный, вам нужно объявить класс абстрактным и предоставить реализацию абстрактного члена в его унаследованном классе (подклассе).

Вам необходимо предоставить реализацию абстрактному члену класса в подклассе, где вы собираетесь изменить поведение статического метода, также объявленного как абстрактный, который ограничен базовым классом, что не является правильным

Sankar
источник
Как это отвечает на вопрос? ОП спрашивал об интерфейсе, пока вы писали о классе.
Счастливчик
0

Так как статические методы не могут быть унаследованы. Так что нет смысла помещать его в интерфейс. Интерфейс - это контракт, которому должны следовать все его подписчики. Размещение статического метода в интерфейсе заставит подписчиков реализовать его. что теперь становится противоречащим факту, что статические методы не могут быть унаследованы.

ip_x
источник
статические методы всегда наследуются, но их нельзя переопределить.
Akki
0

В Java 8 интерфейсы теперь могут иметь статические методы.

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

Требование, чтобы интерфейсы не могли иметь реализации, также было смягчено. Интерфейсы теперь могут объявлять реализации метода «по умолчанию», которые похожи на обычные реализации с одним исключением: если вы наследуете как реализацию по умолчанию от интерфейса, так и обычную реализацию от суперкласса, реализация суперкласса всегда будет иметь приоритет.

Ishara
источник
О, МОЙ БОГ! У нас есть серьезный программист (и я, комментатор), отвечающий (и я, комментирующий) на 10-летний вопрос.
Мохамед Анис A
Я не заметил дату :)
Ишар
Ха - ха! Нет проблем!
Мохамед
-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 не было бы способа выяснить, следует ли вызывать pay на BusinessAcount или CustomerAccount.

FlySwat
источник
То, что статические методы не имеют смысла в этом примере, не означает, что они не имеют смысла в любом примере. В вашем примере, если в интерфейсе IPayable есть статический метод «IncrementPayables», который отслеживает, сколько было добавлено кредиторской задолженности, это был бы реальный вариант использования. Конечно, всегда можно использовать абстрактный класс, но это не то, к чему вы обращались. Ваш пример сам по себе не подрывает статические методы в интерфейсах.
Cruncher