it-swarm-ru.tech

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

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

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

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

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

230
Mo.

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

В любом случае, разработчик просто объявляет интерфейс с именем 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() {...}
});
290
Jesse Glick

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

71
ColinD

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

13
Bas Leijdekkers

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

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

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

9
Clinton N. Dreisbach

Ответ Джесси близок, но я думаю, что есть лучший код, чтобы продемонстрировать, почему внутренний интерфейс может быть полезен. Посмотрите на код ниже, прежде чем читать дальше. Можете ли вы найти, почему внутренний интерфейс полезен? Ответ в том, что класс DoSomethingAlready можно создать с помощью любой класс, который реализует A и C; не только конкретный класс зоопарка. Конечно, это может быть достигнуто, даже если 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;
  }
}
6
user1982892

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

Map.Entry

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

Статическая Вложенная запись в блоге Inerfaces

3
Henry B

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

0
Pindatjuh

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

интерфейс, определенный внутри другого интерфейса, будет неявно public static.

0
Danylo Volokh

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

0
basszero