Почему нет статических методов в интерфейсах, но статические поля и внутренние классы в порядке? [Предварительно Java8]
Здесь было задано несколько вопросов о том, почему вы не можете определить статические методы в интерфейсах, но ни один из них не решает основную несогласованность: почему вы можете определить статические поля и статические внутренние типы в интерфейсе, но не статические методы?
Статические внутренние типы, возможно, не являются справедливым сравнением, поскольку это просто синтаксический сахар, который генерирует новый класс, но почему поля, а не методы?
Аргумент против статических методов в интерфейсах заключается в том, что он нарушает стратегию разрешения виртуальных таблиц, используемую JVM, но разве это не должно применяться в равной степени к статическим полям, то есть компилятор может просто встроить его?
Последовательность - это то, чего я желаю, и Java должен был либо не поддерживать никакой статики какой-либо формы в интерфейсе, либо это должно быть согласованным и разрешать их.
15 ответов
Было сделано официальное предложение разрешить использование статических методов в интерфейсах в Java 7. Это предложение делается в рамках Project Coin.
Мое личное мнение, что это отличная идея. В реализации нет технических трудностей, и это очень логично и разумно. В Project Coin есть несколько предложений, которые, я надеюсь, никогда не станут частью языка Java, но это то, что может очистить многие API. Например, Collections
класс имеет статические методы для манипулирования любым List
реализация; они могут быть включены в List
интерфейс.
Обновление: в подкасте Java Posse № 234 Джо Д'арси кратко упомянул это предложение, сказав, что оно "сложное" и, вероятно, не войдет в него под Project Coin.
Обновление: хотя они не попали в Project Coin для Java 7, Java 8 поддерживает статические функции в интерфейсах.
Я собираюсь пойти с моей любимой теорией с этой, которая заключается в том, что отсутствие согласованности в этом случае является вопросом удобства, а не дизайна или необходимости, так как я не слышал убедительных аргументов, что это был один из этих двух,
Статические поля есть (а), потому что они были там в JDK 1.0, и многие хитрые решения были приняты в JDK 1.0, и (б) статические конечные поля в интерфейсах - это то, что java было ближе всего к константам в то время.
Статические внутренние классы в интерфейсах были разрешены, потому что это чистый синтаксический сахар - внутренний класс на самом деле не имеет ничего общего с родительским классом.
Поэтому статические методы не допускаются просто потому, что для этого нет веских причин; последовательность не является достаточно убедительной, чтобы изменить статус-кво.
Конечно, это может быть разрешено в будущих версиях JLS, ничего не нарушая.
Нет смысла объявлять статический метод в интерфейсе. Они не могут быть выполнены обычным вызовом MyInterface.staticMethod(). (РЕДАКТИРОВАТЬ: так как это последнее предложение сбило с толку некоторых людей, вызов MyClass.staticMethod() точно выполняет реализацию staticMethod на MyClass, который, если MyClass является интерфейсом, не может существовать!) Если вы вызываете их, указывая класс реализации MyImplementor.staticMethod() тогда вы должны знать фактический класс, поэтому не имеет значения, содержит ли его интерфейс или нет.
Что еще более важно, статические методы никогда не переопределяются, и если вы попытаетесь сделать:
MyInterface var = new MyImplementingClass();
var.staticMethod();
правила для static говорят о том, что метод, определенный в объявленном типе var, должен быть выполнен. Поскольку это интерфейс, это невозможно.
Конечно, вы всегда можете удалить статическое ключевое слово из метода. Все будет работать нормально Возможно, вам придется подавить некоторые предупреждения, если он вызывается из метода экземпляра.
Чтобы ответить на некоторые комментарии ниже, причина, по которой вы не можете выполнить "result=MyInterface.staticMethod()", заключается в том, что ему нужно будет выполнить версию метода, определенного в MyInterface. Но не может быть версии, определенной в MyInterface, потому что это интерфейс. У него нет кода по определению.
Назначение интерфейсов - определить контракт без предоставления реализации. Поэтому у вас не может быть статических методов, потому что они должны иметь реализацию уже в интерфейсе, поскольку вы не можете переопределить статические методы. Что касается полей, только статические final fields
позволены, которые, по сути, являются константами (в 1.5+ вы также можете иметь перечисления в интерфейсах). Константы помогают определить интерфейс без магических чисел.
Кстати, нет необходимости явно указывать static final
модификаторы для полей в интерфейсах, потому что разрешены только статические конечные поля.
Это старая ветка, но это очень важный вопрос для всех. Так как я заметил это только сегодня, поэтому я пытаюсь объяснить это более понятным способом:
Основная цель интерфейса - предоставить что-то, что не может быть реализовано, поэтому, если они обеспечивают
статические методы должны быть разрешены
затем вы можете вызвать этот метод с помощью interfaceName.staticMethodName (), но это не реализованный метод и ничего не содержащий. Поэтому бесполезно разрешать статические методы. Поэтому они не обеспечивают это вообще.
статические поля разрешены
поскольку поля не реализуемы, под реализуемым я имею в виду, что вы не можете выполнять какие-либо логические операции в поле, вы можете выполнять операции над полем. Таким образом, вы не меняете поведение поля, поэтому они разрешены.
Внутренние занятия разрешены
Внутренние классы разрешены, потому что после компиляции создается другой файл класса класса Inner, скажем InterfaceName $ InnerClassName.class, так что в основном вы предоставляете реализацию в разных сущностях все вместе, но не в интерфейсе. Так что реализация во внутренних классах обеспечена.
Я надеюсь, что это поможет.
На самом деле иногда есть причины, по которым кто-то может извлечь выгоду из статических методов. Их можно использовать как фабричные методы для классов, которые реализуют интерфейс. Например, по этой причине у нас теперь есть интерфейс Collection и класс Collections в openjdk. Таким образом, есть обходные пути, как всегда - предоставить другому классу приватный конструктор, который будет служить "пространством имен" для статических методов.
До Java 5 обычное использование статических полей было:
interface HtmlConstants {
static String OPEN = "<";
static String SLASH_OPEN = "</";
static String CLOSE = ">";
static String SLASH_CLOSE = " />";
static String HTML = "html";
static String BODY = "body";
...
}
public class HtmlBuilder implements HtmlConstants { // implements ?!?
public String buildHtml() {
StringBuffer sb = new StringBuffer();
sb.append(OPEN).append(HTML).append(CLOSE);
sb.append(OPEN).append(BODY).append(CLOSE);
...
sb.append(SLASH_OPEN).append(BODY).append(CLOSE);
sb.append(SLASH_OPEN).append(HTML).append(CLOSE);
return sb.toString();
}
}
Это означало, что HtmlBuilder не должен был бы квалифицировать каждую константу, поэтому он мог бы использовать OPEN вместо HtmlConstants.OPEN
Использование орудий таким образом в конечном итоге сбивает с толку.
Теперь с Java 5 у нас есть статический синтаксис импорта для достижения того же эффекта:
private final class HtmlConstants {
...
private HtmlConstants() { /* empty */ }
}
import static HtmlConstants.*;
public class HtmlBuilder { // no longer uses implements
...
}
Нет никакой реальной причины для отсутствия статических методов в интерфейсах, кроме: разработчики языка Java не хотели этого. С технической точки зрения имело бы смысл их разрешить. Ведь у абстрактного класса они тоже могут быть. Я предполагаю, но не проверял это, что вы можете "вручную" создавать байт-код, где интерфейс имеет статический метод, и он должен работать без проблем, вызывая метод и / или использовать интерфейс как обычно.
Я часто задаюсь вопросом, зачем вообще статические методы? У них есть свои применения, но методы уровня пакета / пространства имен, вероятно, охватят 80 из того, для чего используются статические методы.
На ум приходят две основные причины:
Статические методы в Java не могут быть переопределены подклассами, и это гораздо сложнее для методов, чем статические поля. На практике я никогда не хотел переопределять поле в подклассе, но я переопределяю методы все время. Таким образом, наличие статических методов не позволяет классу, реализующему интерфейс, предоставить собственную реализацию этого метода, что в значительной степени противоречит цели использования интерфейса.
Интерфейсы не должны иметь код; для этого нужны абстрактные классы. Весь смысл интерфейса состоит в том, чтобы позволить вам говорить о возможно не связанных объектах, которые случаются со всеми, имеют определенный набор методов. На самом деле обеспечение реализации этих методов выходит за пределы того, для чего предназначены интерфейсы.
Статические методы привязаны к классу. В Java интерфейс технически не является классом, это тип, но не класс (следовательно, ключевое слово реализует, а интерфейсы не расширяют Object). Поскольку интерфейсы не являются классами, они не могут иметь статические методы, потому что нет фактического класса для присоединения.
Вы можете вызвать InterfaceName.class, чтобы получить объект Class, соответствующий интерфейсу, но класс Class специально указывает, что он представляет классы и интерфейсы в приложении Java. Однако сам интерфейс не рассматривается как класс, и поэтому вы не можете присоединить статический метод.
Причина в том, что все методы, определенные в интерфейсе, являются абстрактными, независимо от того, явно ли вы объявили этот модификатор. Абстрактный статический метод не является допустимой комбинацией модификаторов, так как статические методы не могут быть переопределены.
Что касается того, почему интерфейсы допускают статические поля. У меня такое чувство, что это следует считать "особенностью". Единственная возможность, о которой я могу подумать, - это сгруппировать константы, которые могут заинтересовать реализации интерфейса.
Я согласен, что последовательность была бы лучшим подходом. В интерфейсе нельзя использовать статические элементы.
Я полагаю, что к статическим методам можно получить доступ без создания объекта, а интерфейс не позволяет создавать объект, поскольку ограничивает программистов от непосредственного использования методов интерфейса, а не от его реализованного класса. Но если вы определяете статический метод в интерфейсе, вы можете получить к нему доступ напрямую без его реализации. Таким образом, статические методы не допускаются в интерфейсах. Я не думаю, что последовательность должна быть проблемой.
Статический метод интерфейса Java 1.8 видим только интерфейсным методам. Если мы удалим метод methodSta1() из класса InterfaceExample, мы не сможем использовать его для объекта InterfaceExample. Однако, как и другие статические методы, мы можем использовать статические методы интерфейса, используя имя класса. Например, допустимым оператором будет: exp1.methodSta1();
Поэтому, посмотрев пример ниже, мы можем сказать: 1) Статический метод интерфейса Java является частью интерфейса, мы не можем использовать его для объектов класса реализации.
2) Статические методы интерфейса Java хороши для предоставления служебных методов, например, проверки нуля, сортировки коллекции, журнала и т. Д.
3) Статический метод интерфейса Java помогает нам обеспечивать безопасность, не позволяя классам реализации (InterfaceExample) переопределять их.
4) Мы не можем определить интерфейсный статический метод для методов класса Object, мы получим ошибку компилятора как "Этот статический метод не может скрыть метод экземпляра от Object". Это потому, что это не разрешено в Java, так как Object является базовым классом для всех классов, и у нас не может быть одного статического метода уровня класса и другого метода экземпляра с одинаковой сигнатурой.
5) Мы можем использовать статические методы интерфейса Java, чтобы удалить служебные классы, такие как Коллекции, и переместить все его статические методы в соответствующий интерфейс, который было бы легко найти и использовать.
public class InterfaceExample implements exp1 {
@Override
public void method() {
System.out.println("From method()");
}
public static void main(String[] args) {
new InterfaceExample().method2();
InterfaceExample.methodSta2(); // <--------------------------- would not compile
// methodSta1(); // <--------------------------- would not compile
exp1.methodSta1();
}
static void methodSta2() { // <-- it compile successfully but it can't be overridden in child classes
System.out.println("========= InterfaceExample :: from methodSta2() ======");
}
}
interface exp1 {
void method();
//protected void method1(); // <-- error
//private void method2(); // <-- error
//static void methodSta1(); // <-- error it require body in java 1.8
static void methodSta1() { // <-- it compile successfully but it can't be overridden in child classes
System.out.println("========= exp1:: from methodSta1() ======");
}
static void methodSta2() { // <-- it compile successfully but it can't be overridden in child classes
System.out.println("========= exp1:: from methodSta2() ======");
}
default void method2() { System.out.println("--- exp1:: from method2() ---");}
//synchronized default void method3() { System.out.println("---");} // <-- Illegal modifier for the interface method method3; only public, abstract, default, static
// and strictfp are permitted
//final default void method3() { System.out.println("---");} // <-- error
}
Только статические конечные поля могут быть объявлены в интерфейсе (во многом как методы, которые являются общедоступными, даже если вы не включите ключевое слово "public", статические поля являются "final" с ключевым словом или без него).
Это только значения, и они будут скопированы буквально, где бы они ни использовались во время компиляции, поэтому вы никогда не вызовете статические поля во время выполнения. Наличие статического метода не будет иметь такой же семантики, так как это будет включать в себя вызов интерфейса без реализации, что Java не позволяет.