Зачем использовать статический вложенный интерфейс в Java?

Я только что нашел статический вложенный интерфейс в нашей кодовой базе.

class Foo {
    public static interface Bar {
        /* snip */
    }
    /* snip */
}

Я такого раньше не видел. Оригинальный разработчик недоступен. Поэтому я должен спросить ТАК:

Какова семантика статического интерфейса? Что изменится, если я удалю static? Зачем кому-то это делать?

Это не внутренний интерфейс: это интерфейс вложенный. Внутренний имеет особое значение в Java.

user207421 11.01.2017 06:29
Пользовательский скаляр GraphQL
Пользовательский скаляр GraphQL
Листовые узлы системы типов GraphQL называются скалярами. Достигнув скалярного типа, невозможно спуститься дальше по иерархии типов. Скалярный тип...
Как вычислять биты и понимать побитовые операторы в Java - объяснение с примерами
Как вычислять биты и понимать побитовые операторы в Java - объяснение с примерами
В компьютерном программировании биты играют важнейшую роль в представлении и манипулировании данными на двоичном уровне. Побитовые операции...
Поднятие тревоги для долго выполняющихся методов в Spring Boot
Поднятие тревоги для долго выполняющихся методов в Spring Boot
Приходилось ли вам сталкиваться с требованиями, в которых вас могли попросить поднять тревогу или выдать ошибку, когда метод Java занимает больше...
Полный курс Java для разработчиков веб-сайтов и приложений
Полный курс Java для разработчиков веб-сайтов и приложений
Получите сертификат Java Web и Application Developer, используя наш курс.
235
1
78 368
11
Перейти к ответу Данный вопрос помечен как решенный

Ответы 11

Внутренний интерфейс должен быть статическим, чтобы к нему можно было получить доступ. Интерфейс связан не с экземплярами класса, а с самим классом, поэтому доступ к нему будет осуществляться с помощью Foo.Bar, например:

public class Baz implements Foo.Bar {
   ...
}

В большинстве случаев это не отличается от статического внутреннего класса.

Вложенный интерфейс автоматически статичен, независимо от того, записано ли ключевое слово или нет.

Paŭlo Ebermann 16.07.2011 00:02

Нам действительно нужен способ, чтобы сообщество проголосовало за принятие другого ответа: stackoverflow.com/a/74400/632951

Pacerier 06.03.2012 09:34

@ ClintonN.Dreisbach Вы можете подробнее объяснить, что означает The interface isn't associated with instances of the class, but with the class itself, я не понял

Kasun Siyambalapitiya 07.07.2017 14:40

Чтобы ответить на ваш вопрос прямо, посмотрите на Map.Entry.

Map.Entry

также это может быть полезно

Запись в блоге о статических вложенных интерфейсах

Это пример, но не ответ.

Paŭlo Ebermann 16.07.2011 00:05

Я часто использую Map.Entry для создания «парных» объектов. Это разоблачено. Реализация Pair имеет две точки зрения, но дело не в этом. Map.Entry может быть внутренним, но я использую его снаружи.

Ravindranath Akila 29.07.2014 08:01

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

«Статическое внутреннее» - это терминологическое противоречие. Вложенные классы могут быть статическими или внутренними либо.

user207421 12.08.2015 01:03

Статический означает, что любая часть класса пакета (проекта) может получить к нему доступ без использования указателя. Это может быть полезно или мешает в зависимости от ситуации.

Прекрасным примером полезности «статических» методов является класс Math. Все методы в Math статичны. Это означает, что вам не нужно изо всех сил создавать новый экземпляр, объявлять переменные и сохранять их в еще большем количестве переменных, вы можете просто ввести свои данные и получить результат.

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

Статические методы хороши для одноразовых возвратов и быстрых вычислений или легко получаемых данных.

Я знаю, что такое статика. Я просто никогда раньше не видел этого в интерфейсе. Поэтому ваш вопрос не по теме - извините

Mo. 16.09.2008 16:45

Думаю, его ответ не по теме.

Koray Tugay 22.01.2014 12:47

В Java статический интерфейс / класс позволяет использовать интерфейс / класс как класс верхнего уровня, то есть он может быть объявлен другими классами. Итак, вы можете:

class Bob
{
  void FuncA ()
  {
    Foo.Bar foobar;
  }
}

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

Описание типов классов в Java.

Без статики это компиляция делает. «Статика» избыточна.

user207421 12.08.2015 01:04

@EJP: Согласно той статье, на которую я ссылался, без статики внутренний класс может использоваться только классом-владельцем, а не чем-либо за пределами класса-владельца. Статический делает его вложенным классом верхнего уровня и может использоваться объектами за пределами класса-владельца (по крайней мере, согласно статье). Статическое значение не является полностью избыточным и влияет на область действия внутреннего класса. Это относится к классам, интерфейсы всегда могут действовать как объекты верхнего уровня (для проверки необходимо прочитать спецификацию языка). Возможно, мне следовало разделить часть своего ответа на интерфейс и класс (мое плохое).

Skizz 12.08.2015 01:44
Ответ принят как подходящий

Ключевое слово static в приведенном выше примере является избыточным (вложенный интерфейс автоматически становится «статическим») и может быть удален без влияния на семантику; Я бы рекомендовал его удалить. То же самое касается "общедоступных" в методах интерфейса и "общедоступного финала" в полях интерфейса - модификаторы избыточны и просто добавляют беспорядок в исходный код.

В любом случае разработчик просто объявляет интерфейс с именем Foo.Bar. Дальнейшей связи с включающим классом нет, за исключением того, что код, не имеющий доступа к Foo, также не сможет получить доступ к Foo.Bar. (Из исходного кода - байт-код или отражение могут получить доступ к Foo.Bar, даже если Foo является закрытым для пакета!)

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

public class Foo {
    public interface Bar {
        void callback();
    }
    public static void registerCallback(Bar bar) {...}
}
// ...elsewhere...
Foo.registerCallback(new Foo.Bar() {
    public void callback() {...}
});

В ответе Джесси Глика, что это означает: (Из исходного кода - байт-код или отражение могут получить доступ к Foo.Bar, даже если Foo является закрытым для пакета!).

Vasu 25.03.2010 09:03

Kailash, доступ к частным методам можно получить через отражение (в пакете отражения) и через прямой доступ к байт-коду сгенерированных файлов .class.

gmoore 28.04.2010 20:35

Под «байт-кодом ... может получить доступ к Foo.Bar» я имею в виду, что скомпилированный класс, ссылающийся на Foo.Bar, может быть загружен и запущен, даже если он не может ссылаться на Foo. Это могло произойти, если класс был скомпилирован ранее, когда Foo был общедоступным, или если класс был собран вручную или скомпилирован с какого-либо языка, отличного от Java, и т. д. Однако компилятор Java проверяет модификатор доступа для включающего класса даже если результирующий байт-код не будет ссылаться на этот включающий класс.

Jesse Glick 08.06.2011 21:27

В строке 3 это будет: "public final" в полях интерфейса -> "public статический final" в полях интерфейса?

Rainning 26.04.2019 21:03

@Raining Это должен быть public static final, поскольку interface не может быть создан.

Aman 18.10.2020 18:50

Интерфейсы-члены неявно статичны. Модификатор static в вашем примере можно удалить без изменения семантики кода. См. Также Спецификацию языка Java 8.5.1. Объявления типов статических элементов

Это не «внутренний интерфейс»: это вложенный интерфейс. Внутренний имеет особое значение в Java.

user207421 11.01.2017 06:27

@EJP, я читал разные блоги, используя эти термины как синонимы. Есть ли внутренний интерфейс? Чем они отличаются от вложенного интерфейса?

Number945 11.07.2018 03:56

@BreakingBenjamin, вложенный интерфейс означает статический. Существует внутренний класс и вложенный класс, но не внутренний интерфейс. Даже если это так, он должен называться вложенным интерфейсом. Внутренний - нестатический, а вложенный - статический.

Sundar Rajan 11.08.2018 16:26

На вопрос был дан ответ, но одна из веских причин для использования вложенного интерфейса заключается в том, что его функция напрямую связана с классом, в котором он находится. Хорошим примером этого является Listener. Если у вас есть класс Foo, и вы хотите, чтобы другие классы могли прослушивать события на нем, вы могли бы объявить интерфейс с именем FooListener, что нормально, но, вероятно, было бы более ясно объявить вложенный интерфейс и иметь эти другие классы реализовать Foo.Listener (вместе с этим неплох и вложенный класс Foo.Event).

Типичный пример - java.util.Map.Entry (интерфейс, вложенный в другой интерфейс).

Paŭlo Ebermann 16.07.2011 00:03

Я знаю, что это старая тема, но я бы предпочел, чтобы внешний класс находился в собственном пакете, а любые дополнительные интерфейсы (например, Map.Entry) или классы также входили в этот пакет. Я говорю это, потому что мне нравится, чтобы мои занятия были короткими и по существу. Также читатель может увидеть, какие другие сущности связаны с классом, посмотрев на классы в пакете. Наверное, у меня был бы пакет java.collections.map для карт. Это объектно-ориентированный подход и модульность. В java.util слишком много всего. util похож на common - запах IMO

David Kerr 14.03.2012 17:27

@Shaggy: В java.util определенно слишком много всего. Тем не менее, я не думаю, что разбиение его на такие мелкозернистые пакеты, как вы предлагаете, также является идеальным.

ColinD 14.03.2012 22:25

@DavidKerr Предположим, вы называете этот интерфейс java.util.MapEntry вне его собственного пакета. Первый рефлекс: какой интерфейс связан с этим классом? Я смотрю в класс. Если javadoc не ссылается на этот интерфейс, я понятия не имею об их отношении. Плюс люди не смотрят на импортный пакет. У каждого свое мнение. Никто не прав, никто не виноват

Raymond Chenon 18.08.2016 13:43

@RaymondChenon часть того, что я сказал, заключалась в том, чтобы поместить Map и связанные с ней классы, например Map.Entry в отдельный пакет, например java.collections.map. Таким образом, весь код, связанный с картой, находится в одном месте (пакете), и класс Map верхнего уровня не перегружается. Возможно, я бы поместил связанные реализации (HashMap и т. д.) В один и тот же пакет.

David Kerr 22.08.2016 11:34

Это не «внутренний интерфейс»: это вложенный интерфейс. Внутренний имеет особое значение в Java.

user207421 11.01.2017 06:27

Ответ Джесси близок, но я думаю, что есть лучший код, демонстрирующий, почему внутренний интерфейс может быть полезен. Прежде чем читать дальше, посмотрите на приведенный ниже код. Сможете ли вы понять, чем полезен внутренний интерфейс? Ответ заключается в том, что класс DoSomethingAlready может быть создан с помощью класса любой, который реализует A и C; не только конкретный класс Zoo. Конечно, этого можно достичь, даже если AC не является внутренним, но представьте, что вы объединяете более длинные имена (не только A и C) и делаете это для других комбинаций (скажем, A и B, C и B и т. д.), И вы легко увидеть, как все выходит из-под контроля. Не говоря уже о том, что люди, просматривающие ваше дерево исходных текстов, будут поражены интерфейсами, которые имеют смысл только в одном классе. Итак, резюмируя, внутренний интерфейс позволяет создавать пользовательские типы и улучшает их инкапсуляцию.

class ConcreteA implements A {
 :
}

class ConcreteB implements B {
 :
}

class ConcreteC implements C {
 :
}

class Zoo implements A, C {
 :
}

class DoSomethingAlready {
  interface AC extends A, C { }

  private final AC ac;

  DoSomethingAlready(AC ac) {
    this.ac = ac;
  }
}

Это нонсенс. Класс Zoo реализует нет интерфейс AC, поэтому экземпляры Zoo не могут быть переданы конструктору DoSomethingAlready, который ожидает AC. Тот факт, что AC расширяет как A, так и C, не означает, что классы, реализующие A и C, волшебным образом также реализуют AC.

Holger 30.06.2016 12:19

Если вы измените класс Foo на интерфейс Foo, ключевое слово public в приведенном выше примере также будет избыточным, потому что

interface defined inside another interface will implicitly public static.

Не ответил на вопрос

Rainning 27.04.2019 10:32

В 1998 году Филип Вадлер предложил различие между статическими и нестатическими интерфейсами.

So far as I can see, the only difference in making an interface non-static is that it can now include non-static inner classes; so the change would not render invalid any existing Java programs.

Например, он предложил решение Проблема выражения, которое представляет собой несоответствие между выражением «сколько может выразить ваш язык» с одной стороны и выражением «термины, которые вы пытаетесь представить на своем языке» с другой стороны.

Пример разницы между статическими и нестатическими вложенными интерфейсами можно увидеть в его образец кода:

// This code does NOT compile
class LangF<This extends LangF<This>> {
    interface Visitor<R> {
        public R forNum(int n);
    }

    interface Exp {
        // since Exp is non-static, it can refer to the type bound to This
        public <R> R visit(This.Visitor<R> v);
    }
}

Его предложение так и не было реализовано в Java 1.5.0. Следовательно, все остальные ответы верны: нет разницы между статическими и нестатическими вложенными интерфейсами.

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

user207421 12.08.2015 01:10

Другие вопросы по теме