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

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

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

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

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

Ответ 1

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

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

Ответ 2

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

Ответ 3

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

Ответ 4

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

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

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

Ответ 5

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

Ответ 7

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

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

Ответ 8

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

Ответ 9

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

Ответ 10

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

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

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

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

Ответ 11

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

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

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

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