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

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

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

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

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

15 ответов


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

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


обновление: на Java Posse Подкаст #234, Джо Д'Арси кратко упомянул о предложении, сказав, что оно "сложное" и, вероятно, не будет сделано в рамках проекта Coin.


обновление: пока они не вошли в проект Монета для Java 7, Java 8 поддерживает статические функции в интерфейсах.


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

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

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

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


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

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

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

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

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

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


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

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


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

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

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

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

статические поля не допускается

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

внутренние классы не допускается

допускаются внутренние классы потому что после компиляции создается другой файл класса внутреннего класса say InterfaceName$InnerClassName.класс!--24-->, поэтому в основном вы предоставляете реализацию в разных сущностях все вместе, но не в интерфейсе. Так обеспечивается реализация во внутренних классах.

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


на самом деле иногда есть причины, по которым кто-то может извлечь выгоду из статических методов. Их можно использовать в качестве заводских методов для классов, реализующих интерфейс. Например, по этой причине у нас есть интерфейс коллекции и класс 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 не нужно было бы квалифицировать каждую константу, поэтому она могла бы использовать открыть вместо HtmlConstants.Открыть

использование инструментов таким образом в конечном итоге запутывает.

теперь с Java 5, у нас есть импорт статический!--6--> синтаксис для достижения того же эффекта:

private final class HtmlConstants {
    ...
    private HtmlConstants() { /* empty */ }
}

import static HtmlConstants.*;
public class HtmlBuilder { // no longer uses implements
    ...
}

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


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


на ум приходят две основные причины:

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

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


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

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


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

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


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

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

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


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


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

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

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

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

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