Почему нет статических методов в интерфейсах, но статические поля и внутренние классы в порядке? [Предварительно Java8]

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

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

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

Консистенция - это то, чего я хочу, и Java должна либо не поддерживать статику какой-либо формы внутри интерфейса, либо должна быть последовательной и допускать их.

Ответы

Ответ 1

Было сделано официальное предложение чтобы разрешить статические методы в интерфейсах в Java 7. Это предложение выполняется в Монета проекта.

Мое личное мнение заключается в том, что это отличная идея. В реализации нет технических трудностей, и это очень логично, разумно. В Project Coin есть несколько предложений, которые, я надеюсь, никогда не станут частью языка Java, но это тот, который может очистить множество API. Например, класс Collections имеет статические методы для управления любой реализацией List; они могут быть включены в интерфейс List.


Обновление: в Java Posse Podcast # 234, Joe D'arcy кратко изложил это предложение, сказав, что это "сложный" и, возможно, не попадет под проектную монету.


Обновление. Хотя они не попали в Project Coin для Java 7, Java 8 поддерживает статические функции в интерфейсах.

Ответ 2

Я собираюсь пойти с моей теорией домашних животных с этим, то есть, что отсутствие последовательности в этом случае является вопросом удобства, а не дизайном или необходимостью, поскольку я не слышал убедительных аргументов, что это было либо из этих двух.

Статические поля есть (а), потому что они были там в JDK 1.0, и многие хитроумные решения были сделаны в JDK 1.0, и (b) статические конечные поля в интерфейсах являются наиболее близкими к тому, что java приходилось на константы в то время.

Статические внутренние классы в интерфейсах были разрешены, потому что этот чистый синтаксический сахар - внутренний класс на самом деле не имеет ничего общего с родительским классом.

Таким образом, статические методы не допускаются просто потому, что нет веских оснований для этого; согласованность не является достаточно убедительной, чтобы изменить статус-кво.

Конечно, это может быть разрешено в будущих версиях JLS, не нарушая ничего.

Ответ 3

Никогда не бывает смысла объявлять статический метод в интерфейсе. Они не могут быть выполнены обычным вызовом MyInterface.staticMethod(). (EDIT: Поскольку это последнее предложение смутило некоторых людей, вызов MyClass.staticMethod() выполняет точно реализацию staticMethod на MyClass, который, если MyClass - это интерфейс, не может существовать!) Если вы вызываете их, указав класс реализации MyImplementor.staticMethod() то вы должны знать фактический класс, поэтому не имеет значения, содержит ли он этот интерфейс или нет.

Что еще более важно, статические методы никогда не переопределяются, и если вы попытаетесь сделать:

MyInterface var = new MyImplementingClass();
var.staticMethod();

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

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

Чтобы ответить на некоторые из приведенных ниже комментариев, причина, по которой вы не можете выполнить "result = MyInterface.staticMethod()", заключается в том, что она должна была выполнить версию метода, определенного в MyInterface. Но не может быть версии, определенной в MyInterface, потому что это интерфейс. Он не имеет кода по определению.

Ответ 4

Целью интерфейсов является определение контракта без предоставления реализации. Поэтому у вас не может быть статических методов, поскольку они должны иметь реализацию уже в интерфейсе, поскольку вы не можете переопределить статические методы. Что касается полей, допустимы только статические final fields, которые по существу являются константами (в 1.5+ вы также можете иметь перечисления в интерфейсах). Константы там, чтобы помочь определить интерфейс без магических чисел.

Кстати, нет необходимости явно указывать модификаторы static final для полей в интерфейсах, потому что допускаются только статические конечные поля.

Ответ 5

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

Основная цель интерфейса - предоставить что-то, что невозможно реализовать, поэтому, если они предоставляют

статические методы, которые необходимо разрешить

то вы можете вызвать этот метод, используя interfaceName.staticMethodName(), но это не реализуемый метод и ничего не содержит. Поэтому бесполезно допускать статические методы. Поэтому они вообще этого не предоставляют.

статические поля разрешены

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

Разрешены внутренние классы

Внутренние классы разрешены, потому что после компиляции создается классный класс класса Inner: InterfaceName $InnerClassName.class, поэтому в основном вы предоставляете реализацию в разных сущностях вместе, но не в интерфейсе. Поэтому предоставляется реализация в классах Inner.

Надеюсь, это поможет.

Ответ 6

До появления 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
    ...
}

Ответ 7

На самом деле иногда есть причины, по которым кто-то может воспользоваться статическими методами. Они могут использоваться как методы factory для классов, реализующих интерфейс. Например, причина в том, что у нас есть интерфейс Collection и класс Collections в openjdk. Таким образом, как правило, обходные пути - предоставить другому классу частный конструктор, который будет служить "пространством имен" для статических методов.

Ответ 8

Нет никакой реальной причины не иметь статических методов в интерфейсах, за исключением: разработчики языка Java этого не хотели. С технической точки зрения было бы разумно позволить им. В конце концов, абстрактный класс также может иметь их. Я предполагаю, но не протестировал его, чтобы вы могли "обработать" байтовый код, где интерфейс имеет статический метод, и он должен работать без проблем, чтобы вызвать метод и/или использовать интерфейс, как обычно.

Ответ 9

Я часто задаюсь вопросом, почему статические методы вообще? У них есть свое применение, но методы уровня пакета/пространства имен, вероятно, охватывают 80 тех статических методов, которые используются для.

Ответ 10

Две основные причины spring:

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

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

Ответ 11

Статические методы привязаны к классу. В Java интерфейс не является технически классом, это тип, но не класс (следовательно, реализация ключевых слов и интерфейсы не расширяют Object). Поскольку интерфейсы не являются классами, они не могут иметь статические методы, потому что для этого не существует соответствующего класса.

Вы можете вызвать InterfaceName.class, чтобы получить объект класса, соответствующий интерфейсу, но класс Class указывает, что он представляет классы и интерфейсы в приложении Java. Однако сам интерфейс не рассматривается как класс, и поэтому вы не можете прикрепить статический метод.

Ответ 12

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

Это только значения и будут копироваться буквально везде, где они используются во время компиляции, поэтому вы никогда не назовете "статические поля" во время выполнения. Наличие статического метода не будет иметь одинаковую семантику, поскольку это связано с вызовом интерфейса без реализации, который Java не позволяет.

Ответ 13

Причина в том, что все методы, определенные в интерфейсе, являются абстрактными независимо от того, явно ли вы объявляете этот модификатор. Абстрактный статический метод не является допустимой комбинацией модификаторов, поскольку статические методы не могут быть переопределены.

Относительно того, почему интерфейсы допускают статические поля. У меня такое чувство, которое следует считать "особенностью". Единственная возможность, о которой я могу думать, - это группировать константы, которые будут интересовать реализации интерфейса.

Я согласен с тем, что последовательность была бы лучшим подходом. Никакие статические члены не должны допускаться в интерфейсе.

Ответ 14

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

Ответ 15

Статический метод интерфейса Java 1.8 видим только для методов интерфейса, если мы удалим метод methodSta1() из класса InterfaceExample, мы не сможем использовать его для объекта InterfaceExample. Однако, как и другие статические методы, мы можем использовать статические методы интерфейса, используя имя класса. Например, действительный оператор будет: exp1.methodSta1();

Итак, посмотрев ниже пример, мы можем сказать: 1) Статический метод интерфейса Java является частью интерфейса, мы не можем использовать его для объектов класса реализации.

2) Статические методы интерфейса Java хороши для предоставления методов утилиты, например, проверка нулевого значения, сортировка коллекции, журнал и т.д.

3) Статический метод интерфейса Java помогает нам обеспечить безопасность, не позволяя классам реализации (InterfaceExample) переопределять их.

4) Мы не можем определить статический метод интерфейса для методов класса Object, мы получим ошибку компилятора как "Этот статический метод не может скрыть метод экземпляра из Object". Это связано с тем, что он не разрешен в java, поскольку Object является базовым классом для всех классов, и мы не можем иметь один статический метод уровня класса и другой метод экземпляра с одной и той же сигнатурой.

5) Мы можем использовать статические методы интерфейса Java для удаления классов утилиты, таких как Collections, и переместить все свои статические методы в соответствующий интерфейс,  это было бы легко найти и использовать.

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
}