Я только что нашел статический вложенный интерфейс в нашей кодовой базе.
class Foo {
public static interface Bar {
/* snip */
}
/* snip */
}
Я такого раньше не видел. Оригинальный разработчик недоступен. Поэтому я должен спросить ТАК:
Какова семантика статического интерфейса? Что изменится, если я удалю static? Зачем кому-то это делать?




Внутренний интерфейс должен быть статическим, чтобы к нему можно было получить доступ. Интерфейс связан не с экземплярами класса, а с самим классом, поэтому доступ к нему будет осуществляться с помощью Foo.Bar, например:
public class Baz implements Foo.Bar {
...
}
В большинстве случаев это не отличается от статического внутреннего класса.
Вложенный интерфейс автоматически статичен, независимо от того, записано ли ключевое слово или нет.
Нам действительно нужен способ, чтобы сообщество проголосовало за принятие другого ответа: stackoverflow.com/a/74400/632951
@ ClintonN.Dreisbach Вы можете подробнее объяснить, что означает The interface isn't associated with instances of the class, but with the class itself, я не понял
Чтобы ответить на ваш вопрос прямо, посмотрите на Map.Entry.
также это может быть полезно
Запись в блоге о статических вложенных интерфейсах
Это пример, но не ответ.
Я часто использую Map.Entry для создания «парных» объектов. Это разоблачено. Реализация Pair имеет две точки зрения, но дело не в этом. Map.Entry может быть внутренним, но я использую его снаружи.
Обычно я вижу статические внутренние классы. Статические внутренние классы не могут ссылаться на содержащиеся классы, тогда как нестатические классы могут. Если вы не столкнетесь с некоторыми конфликтами пакетов (уже есть интерфейс с именем Bar в том же пакете, что и Foo), я думаю, что я бы сделал его собственным файлом. Это также может быть дизайнерское решение для обеспечения логической связи между Foo и Bar. Возможно, автор намеревался использовать Bar только с Foo (хотя статический внутренний интерфейс не обеспечивает этого, просто логическое соединение)
«Статическое внутреннее» - это терминологическое противоречие. Вложенные классы могут быть статическими или внутренними либо.
Статический означает, что любая часть класса пакета (проекта) может получить к нему доступ без использования указателя. Это может быть полезно или мешает в зависимости от ситуации.
Прекрасным примером полезности «статических» методов является класс Math. Все методы в Math статичны. Это означает, что вам не нужно изо всех сил создавать новый экземпляр, объявлять переменные и сохранять их в еще большем количестве переменных, вы можете просто ввести свои данные и получить результат.
Статика не всегда так полезна. Например, если вы проводите сравнение регистра, вы можете захотеть сохранить данные несколькими способами. Вы не можете создать три статических метода с одинаковыми подписями. Вам нужно 3 разных экземпляра, нестатических, а затем вы можете и сравнить, потому что, если он статический, данные не будут меняться вместе с вводом.
Статические методы хороши для одноразовых возвратов и быстрых вычислений или легко получаемых данных.
Я знаю, что такое статика. Я просто никогда раньше не видел этого в интерфейсе. Поэтому ваш вопрос не по теме - извините
Думаю, его ответ не по теме.
В Java статический интерфейс / класс позволяет использовать интерфейс / класс как класс верхнего уровня, то есть он может быть объявлен другими классами. Итак, вы можете:
class Bob
{
void FuncA ()
{
Foo.Bar foobar;
}
}
Без статики вышеперечисленное не скомпилируется. Преимущество этого заключается в том, что вам не нужен новый исходный файл только для объявления интерфейса. Он также визуально связывает интерфейсную панель с классом Foo, поскольку вам нужно написать Foo.Bar, и подразумевает, что класс Foo что-то делает с экземплярами Foo.Bar.
Описание типов классов в Java.
Без статики это компиляция делает. «Статика» избыточна.
@EJP: Согласно той статье, на которую я ссылался, без статики внутренний класс может использоваться только классом-владельцем, а не чем-либо за пределами класса-владельца. Статический делает его вложенным классом верхнего уровня и может использоваться объектами за пределами класса-владельца (по крайней мере, согласно статье). Статическое значение не является полностью избыточным и влияет на область действия внутреннего класса. Это относится к классам, интерфейсы всегда могут действовать как объекты верхнего уровня (для проверки необходимо прочитать спецификацию языка). Возможно, мне следовало разделить часть своего ответа на интерфейс и класс (мое плохое).
Ключевое слово 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 является закрытым для пакета!).
Kailash, доступ к частным методам можно получить через отражение (в пакете отражения) и через прямой доступ к байт-коду сгенерированных файлов .class.
Под «байт-кодом ... может получить доступ к Foo.Bar» я имею в виду, что скомпилированный класс, ссылающийся на Foo.Bar, может быть загружен и запущен, даже если он не может ссылаться на Foo. Это могло произойти, если класс был скомпилирован ранее, когда Foo был общедоступным, или если класс был собран вручную или скомпилирован с какого-либо языка, отличного от Java, и т. д. Однако компилятор Java проверяет модификатор доступа для включающего класса даже если результирующий байт-код не будет ссылаться на этот включающий класс.
В строке 3 это будет: "public final" в полях интерфейса -> "public статический final" в полях интерфейса?
@Raining Это должен быть public static final, поскольку interface не может быть создан.
Интерфейсы-члены неявно статичны. Модификатор static в вашем примере можно удалить без изменения семантики кода. См. Также Спецификацию языка Java 8.5.1. Объявления типов статических элементов
Это не «внутренний интерфейс»: это вложенный интерфейс. Внутренний имеет особое значение в Java.
@EJP, я читал разные блоги, используя эти термины как синонимы. Есть ли внутренний интерфейс? Чем они отличаются от вложенного интерфейса?
@BreakingBenjamin, вложенный интерфейс означает статический. Существует внутренний класс и вложенный класс, но не внутренний интерфейс. Даже если это так, он должен называться вложенным интерфейсом. Внутренний - нестатический, а вложенный - статический.
На вопрос был дан ответ, но одна из веских причин для использования вложенного интерфейса заключается в том, что его функция напрямую связана с классом, в котором он находится. Хорошим примером этого является Listener. Если у вас есть класс Foo, и вы хотите, чтобы другие классы могли прослушивать события на нем, вы могли бы объявить интерфейс с именем FooListener, что нормально, но, вероятно, было бы более ясно объявить вложенный интерфейс и иметь эти другие классы реализовать Foo.Listener (вместе с этим неплох и вложенный класс Foo.Event).
Типичный пример - java.util.Map.Entry (интерфейс, вложенный в другой интерфейс).
Я знаю, что это старая тема, но я бы предпочел, чтобы внешний класс находился в собственном пакете, а любые дополнительные интерфейсы (например, Map.Entry) или классы также входили в этот пакет. Я говорю это, потому что мне нравится, чтобы мои занятия были короткими и по существу. Также читатель может увидеть, какие другие сущности связаны с классом, посмотрев на классы в пакете. Наверное, у меня был бы пакет java.collections.map для карт. Это объектно-ориентированный подход и модульность. В java.util слишком много всего. util похож на common - запах IMO
@Shaggy: В java.util определенно слишком много всего. Тем не менее, я не думаю, что разбиение его на такие мелкозернистые пакеты, как вы предлагаете, также является идеальным.
@DavidKerr Предположим, вы называете этот интерфейс java.util.MapEntry вне его собственного пакета. Первый рефлекс: какой интерфейс связан с этим классом? Я смотрю в класс. Если javadoc не ссылается на этот интерфейс, я понятия не имею об их отношении. Плюс люди не смотрят на импортный пакет. У каждого свое мнение. Никто не прав, никто не виноват
@RaymondChenon часть того, что я сказал, заключалась в том, чтобы поместить Map и связанные с ней классы, например Map.Entry в отдельный пакет, например java.collections.map. Таким образом, весь код, связанный с картой, находится в одном месте (пакете), и класс Map верхнего уровня не перегружается. Возможно, я бы поместил связанные реализации (HashMap и т. д.) В один и тот же пакет.
Это не «внутренний интерфейс»: это вложенный интерфейс. Внутренний имеет особое значение в Java.
Ответ Джесси близок, но я думаю, что есть лучший код, демонстрирующий, почему внутренний интерфейс может быть полезен. Прежде чем читать дальше, посмотрите на приведенный ниже код. Сможете ли вы понять, чем полезен внутренний интерфейс? Ответ заключается в том, что класс 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.
Если вы измените класс Foo на интерфейс Foo, ключевое слово public в приведенном выше примере также будет избыточным, потому что
interface defined inside another interface will implicitly public static.
Не ответил на вопрос
В 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.
Это не внутренний интерфейс: это интерфейс вложенный. Внутренний имеет особое значение в Java.