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

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

  •  02-07-2019
  •  | 
  •  

Вопрос

На этот вопрос уже есть ответ здесь:

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

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

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

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

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

Решение

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

Моё личное мнение - это отличная идея.Технических сложностей в реализации нет, и это очень логично и разумно.В 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();

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

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

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

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

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

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

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

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

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

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

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

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

Внутренние классы разрешены, потому что после компиляции создается другой файл класса внутреннего класса, скажем. ИмяИнтерфейса$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 не нужно будет уточнять каждую константу, поэтому можно использовать ОТКРЫТЬ вместо HtmlConstants.OPEN

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

Теперь с Java 5 у нас есть импорт статики синтаксис для достижения того же эффекта:

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

4) Мы не можем определить статический метод интерфейса для методов класса 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
}
Лицензировано под: CC-BY-SA с атрибуция
Не связан с StackOverflow
scroll top