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

StackOverflow https://stackoverflow.com/questions/21817

Вопрос

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

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

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

Это было полезно?

Решение

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

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

и

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

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

Джава мог разрешить последнее;и на самом деле, начиная с Java 8, это так!

Другие советы

Причина, по которой вы не можете использовать статический метод в интерфейсе, заключается в том, как Java разрешает статические ссылки.Java не будет искать экземпляр класса при попытке выполнить статический метод.Это связано с тем, что статические методы не зависят от экземпляра и, следовательно, могут выполняться прямо из файла класса.Учитывая, что все методы в интерфейсе абстрактны, виртуальной машине придется искать конкретную реализацию интерфейса, чтобы найти код статического метода, чтобы его можно было выполнить.Тогда это противоречит тому, как работает разрешение статических методов, и приведет к несогласованности в языке.

Я отвечу на ваш вопрос примером.Предположим, у нас есть класс Math со статическим методом add.Вы бы назвали этот метод так:

Math.add(2, 3);

Если бы Math был интерфейсом, а не классом, он не мог бы иметь никаких определенных функций.Таким образом, говорить что-то вроде Math.add(2, 3) не имеет смысла.

Причина кроется в принципе проектирования, согласно которому Java не допускает множественного наследования.Проблему множественного наследования можно проиллюстрировать следующим примером:

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

Что же произойдет, если вы вызовете C.x()?Будет ли выполняться A.x() или B.x()?Каждый язык с множественным наследованием должен решать эту проблему.

Интерфейсы допускают в Java своего рода ограниченное множественное наследование.Чтобы избежать описанной выше проблемы, им не разрешено иметь методы.Если мы посмотрим на ту же проблему с интерфейсами и статическими методами:

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

Та же проблема: что произойдет, если вы вызовете C.x()?

Статические методы не являются методами экземпляра.Контекста экземпляра нет, поэтому реализовывать его из интерфейса не имеет смысла.

Теперь 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, чтобы сделать их методами по умолчанию и статическими методами соответственно.

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

Кажется, статический метод в интерфейсе может поддерживаться в Ява 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..., тогда вы сможете избавиться от неоднозначной проблемы.

Интерфейс используется для полиморфизма, который применяется к объектам, а не к типам.Поэтому (как уже отмечалось) нет смысла иметь статический член интерфейса.

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);

}

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

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

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

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

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

С Ява 8, интерфейсы теперь могут иметь статические методы.

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

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

Возможно, поможет пример кода. Я собираюсь использовать 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 долларов США на все эти счета:

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

Итак, теперь вы видите, насколько невероятно полезны интерфейсы.

Они используются только для созданных объектов.Не в статических классах.

Если бы вы сделали оплату статической, при циклическом просмотре IPayable в accountToPay не было бы возможности выяснить, должен ли он вызывать оплату на BusinessAcount или CustomerAccount.

Лицензировано под: CC-BY-SA с атрибуция
Не связан с StackOverflow
scroll top