Почему я не могу объявить статические методы в интерфейсе?
Вопрос
В теме больше всего сказано - в чем причина того, что статические методы нельзя объявить в интерфейсе?
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.