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

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

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

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

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


person Mo.    schedule 16.09.2008    source источник
comment
Это не внутренний интерфейс: это вложенный интерфейс. Внутренний имеет особое значение в Java.   -  person user207421    schedule 11.01.2017


Ответы (11)


Ключевое слово 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() {...}
});
person Jesse Glick    schedule 16.09.2008
comment
В ответе Джесси Глика, что это означает: (Из исходного кода - байт-код или отражение могут получить доступ к Foo.Bar, даже если Foo является закрытым для пакета!). - person Vasu; 25.03.2010
comment
Kaillash, доступ к частным методам можно получить через relfection (в пакете рефлекса) и через прямой доступ к байт-коду сгенерированных файлов .class. - person gmoore; 28.04.2010
comment
Под байт-кодом ... может получить доступ к Foo.Bar, я имею в виду, что скомпилированный класс, ссылающийся на Foo.Bar, может быть загружен и запущен, даже если он не может ссылаться на Foo. Это могло произойти, если класс был скомпилирован в более раннее время, когда Foo был общедоступным, или если класс был собран вручную или скомпилирован с какого-либо языка, отличного от Java, и т. Д. Однако компилятор Java проверяет модификатор доступа для включающего класса даже если результирующий байт-код не будет ссылаться на этот включающий класс. - person Jesse Glick; 08.06.2011
comment
В строке 3 это будет: публичный финал в полях интерфейса - ›публичный статический финал в полях интерфейса? - person Rainning; 26.04.2019
comment
@Raining Это должно быть public static final, поскольку interface не может быть создан. - person Aman; 18.10.2020

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

person ColinD    schedule 16.10.2008
comment
Типичный пример - java.util.Map.Entry (интерфейс, вложенный в другой интерфейс). - person Paŭlo Ebermann; 16.07.2011
comment
Я знаю, что это старая тема, но я бы предпочел, чтобы внешний класс находился в собственном пакете, а любые дополнительные интерфейсы (например, Map.Entry) или классы также входили в этот пакет. Я говорю это, потому что мне нравится, чтобы мои занятия были короткими и по существу. Также читатель может увидеть, какие другие сущности связаны с классом, посмотрев на классы в пакете. Наверное, у меня был бы пакет java.collections.map для карт. Это объектно-ориентированный подход и модульность. java.util слишком много. util это похоже на common - запах ИМО - person David Kerr; 14.03.2012
comment
@Shaggy: java.util определенно слишком много. Тем не менее, я не думаю, что разбиение его на такие мелкозернистые пакеты, как вы предлагаете, также является идеальным. - person ColinD; 14.03.2012
comment
@DavidKerr Предположим, вы вызываете этот интерфейс java.util.MapEntry вне его собственного пакета. Первый рефлекс: какой интерфейс связан с этим классом? Я смотрю в класс. Если javadoc не ссылается на этот интерфейс, я понятия не имею об их отношении. Плюс люди не смотрят импортный пакет. У каждого свое мнение. Никто не прав, никто не ошибается - person Raymond Chenon; 18.08.2016
comment
@RaymondChenon - часть того, что я сказал, заключалась в том, чтобы поместить Map и связанные с ней классы, например Map.Entry в отдельный пакет, например java.collections.map. Таким образом, весь код, связанный с картой, находится в одном месте (пакете), и класс Map верхнего уровня не перегружается. Я бы, возможно, поместил связанные реализации (HashMap и т. Д.) В один и тот же пакет. - person David Kerr; 22.08.2016
comment
Это не «внутренний интерфейс»: это вложенный интерфейс. Внутренний имеет особое значение в Java. - person user207421; 11.01.2017

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

person Bas Leijdekkers    schedule 17.09.2008
comment
Это не «внутренний интерфейс»: это вложенный интерфейс. Внутренний имеет особое значение в Java. - person user207421; 11.01.2017
comment
@EJP, я читал разные блоги, используя эти термины как синонимы. Есть ли внутренний интерфейс? Чем они отличаются от вложенного интерфейса? - person Number945; 11.07.2018
comment
@BreakingBenjamin, вложенный интерфейс означает статический. Существует внутренний класс и вложенный класс, но не внутренний интерфейс. Даже если это так, он должен называться вложенным интерфейсом. Внутренний - нестатический, а вложенный - статический. - person Sundar Rajan; 11.08.2018

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

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

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

person Clinton Dreisbach    schedule 16.09.2008
comment
Вложенный интерфейс автоматически становится статическим, независимо от того, записано ли ключевое слово или нет. - person Paŭlo Ebermann; 16.07.2011
comment
Нам действительно нужен способ, чтобы сообщество проголосовало за принятие другого ответа: stackoverflow.com/a/74400/632951 - person Pacerier; 06.03.2012
comment
@ ClintonN.Dreisbach Вы можете еще подробнее объяснить, что означает The interface isn't associated with instances of the class, but with the class itself, я не понял - person Kasun Siyambalapitiya; 07.07.2017

Ответ Джесси близок, но я думаю, что есть лучший код, демонстрирующий, почему внутренний интерфейс может быть полезен. Прежде чем продолжить, посмотрите на приведенный ниже код. Сможете ли вы понять, чем полезен внутренний интерфейс? Ответ заключается в том, что класс 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;
  }
}
person user1982892    schedule 16.01.2013
comment
Это нонсенс. Класс Zoo не реализует интерфейс AC, поэтому экземпляры Zoo не могут быть переданы конструктору DoSomethingAlready, который ожидает AC. Тот факт, что AC расширяет и A, и C, не означает, что классы, реализующие A и C волшебным образом, также реализуют AC. - person Holger; 30.06.2016

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

Map.Entry

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

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

person Henry B    schedule 16.09.2008
comment
Это пример, но не ответ. - person Paŭlo Ebermann; 16.07.2011
comment
Я часто использую Map.Entry для создания объектов Pair. Это разоблачено. Реализация Pair имеет две точки зрения, но дело не в этом. Map.Entry может быть внутренним, но я использую его снаружи. - person Ravindranath Akila; 29.07.2014

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

person basszero    schedule 16.09.2008
comment
«Статическое внутреннее» - это терминологическое противоречие. Вложенные классы могут быть либо статическими, либо внутренними. - person user207421; 12.08.2015

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

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

person Danylo Volokh    schedule 14.05.2015
comment
Не ответил на вопрос - person Rainning; 27.04.2019

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

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

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

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

// 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. Следовательно, все остальные ответы верны: нет разницы между статическими и нестатическими вложенными интерфейсами.

person Pindatjuh    schedule 11.08.2015
comment
Следует отметить, что все это относится к GJ, который был очень ранним процессором для дженериков в Java. - person user207421; 12.08.2015

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

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

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

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

person Skizz    schedule 16.09.2008
comment
Без статики он компилируется. «Статика» избыточна. - person user207421; 12.08.2015
comment
@EJP: Согласно той статье, на которую я ссылался, без статики внутренний класс может использоваться только классом-владельцем, а не чем-либо за пределами класса-владельца. Статический делает его вложенным классом верхнего уровня и может использоваться объектами за пределами класса-владельца (по крайней мере, согласно статье). Статическое значение не является полностью избыточным и влияет на область действия внутреннего класса. Это относится к классам, интерфейсы всегда могут действовать как объекты верхнего уровня (для проверки необходимо прочитать спецификацию языка). Возможно, мне следовало разделить часть своего ответа на интерфейс и класс (мое плохое). - person Skizz; 12.08.2015

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

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

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

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

person Vordreller    schedule 16.09.2008
comment
Я знаю, что такое статика. Я просто никогда раньше не видел этого в интерфейсе. Поэтому ваш вопрос не по теме - извините - person Mo.; 16.09.2008
comment
Думаю, его ответ не по теме. - person Koray Tugay; 22.01.2014