Навіщо статичний вкладений інтерфейс використовуватиметься на Java?


235

Щойно я знайшов статичний вкладений інтерфейс у нашій кодовій базі.

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

Я ніколи цього не бачив. Оригінальний розробник недоступний. Тому я повинен запитати ТА:

Які семантики стоять за статичним інтерфейсом? Що зміниться, якщо я видалю static? Навіщо хтось це робити?


2
Це не "внутрішній інтерфейс: це вкладений інтерфейс. Внутрішня має специфічне значення на Java.
Маркіз Лорн

Відповіді:


293

Статичне ключове слово у наведеному вище прикладі є зайвим (вкладений інтерфейс автоматично "статичний") і його можна видалити, не впливаючи на семантику; Я рекомендував би його зняти. Те ж саме стосується "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() {...}
});

1
У відповіді Джессі Гліка, що це означає: (З вихідного коду - байт-код або відображення можуть отримати доступ до Foo.Bar, навіть якщо Foo є приватним пакетом!).
Васу,

До приватних методів Kaillash можна отримати доступ через relfection (у пакеті відображення) та через прямий доступ до байтового коду створених файлів .class.
gmoore

2
Під "байт-кодом ... можна отримати доступ до Foo.Bar" я маю на увазі, що компільований клас, що посилається на Foo.Bar, може бути завантажений і запущений, навіть якщо він не міг посилатися на Foo. Це може статися, якщо клас був складений у попередній час, коли Foo був загальнодоступним, або якщо клас було зібрано вручну або складено з якоїсь мови, яка не є Java, і т. Д. Компілятор Java перевіряє модифікатор доступу вкладеного класу навіть коли отриманий байт-код не відноситься до цього класу, що вкладається.
Джессі Глік

@Jesse Чи можна отримати доступ до приватного статичного класу в приватному класі вищого рівня шляхом відображення?
Pacerier

1
@Pacerier: ні. Точніше, ви можете завантажувати клас та перевіряти його членів, але не можете його створювати або викликати методи, не використовуючи setAccessible (true). Іншими словами, це відображається, але не є доступним через рефлексію. Але якщо вкладений статичний клас є загальнодоступним, він доступний за замовчуванням через відображення, навіть якщо він недоступний статично (під час компіляції).
Джессі Глік

72

На питання було дано відповідь, але одна з вагомих причин використовувати вкладений інтерфейс - це якщо його функція безпосередньо пов'язана з класом, в якому він знаходиться. Хорошим прикладом цього є Listener. Якщо у вас був клас, Fooі ви хотіли, щоб інші класи могли слухати події на ньому, ви могли оголосити інтерфейс з ім'ям FooListener, що нормально, але, напевно, було б більш зрозумілим оголосити вкладений інтерфейс і реалізувати ці інші класи Foo.Listener( вкладений клас Foo.Eventнепогано поряд із цим).


11
Типовий приклад java.util.Map.Entry(це інтерфейс, вкладений в інший інтерфейс).
Paŭlo Ebermann

4
Я знаю, що це стара тема, але я вважаю за краще, щоб зовнішній клас був у його власному пакеті, а будь-які додаткові інтерфейси (наприклад Map.Entry) або класи теж були в цьому пакеті. Я говорю це тому, що мені подобається тримати свої заняття короткими і чіткими. Також читач може побачити, які інші об'єкти пов'язані з класом, переглянувши класи в пакеті. Я, мабуть, мав би пакет java.collections.mapдля карт. Йдеться про ОО та модульність. java.utilв ньому занадто багато. utilце як common- запах ІМО
Девід Керр

1
@Shaggy: java.utilзвичайно, в ньому занадто багато. При цьому, я не думаю, що ідеальним є також розбиття його на такі дрібнозернисті пакети, як ви припускаєте.
ColinD

1
@DavidKerr Припустимо, ви викликаєте цей інтерфейс java.util.MapEntryпоза власним пакетом. Перший рефлекс: які інтерфейси пов'язані з цим класом? Я заглядаю в клас. Якщо javadoc не посилається на цей інтерфейс, я не маю поняття про їхнє відношення. Плюс люди не дивляться на пакет імпорту. У кожного своя думка. Ніхто не має права, ніхто не помиляється
Реймонд Шенон

@RaymondChenon частиною того, що я сказав, було скласти Map та пов'язані з ним класи, наприклад Map.Entry, в окремий пакет, наприклад, java.collections.map. Таким чином, весь код, пов’язаний з картою, знаходиться в одному місці (пакет), і клас карти верхнього рівня не обтяжений. Я б, можливо, поклав відповідні реалізації (HashMap тощо) у той самий пакет.
Девід Керр

14

Інтерфейси учасників неявно статичні. Статичний модифікатор у вашому прикладі можна видалити без зміни семантики коду. Дивіться також специфікацію мови Java 8.5.1. Статичні декларації про член


Це не "внутрішній інтерфейс": це вкладений інтерфейс. Внутрішня має специфічне значення на Java.
Маркіз Лорн

@EJP, я читаю різні блоги, використовуючи умови взаємозамінно. Чи існує внутрішній інтерфейс? Чим вони відрізняються від вкладеного інтерфейсу?
Число945

@BreakingBenjamin, Вкладений інтерфейс означає статичний. Існує внутрішній клас і вкладений клас, але не внутрішній інтерфейс. Навіть якщо так, це повинно називатися вкладеним інтерфейсом. Внутрішня - нестатична, а вкладена - статична.
Sundar Rajan

9

Для того, щоб отримати доступ, внутрішній інтерфейс повинен бути статичним. Інтерфейс асоціюється не з екземплярами класу, а з самим класом, тому до нього можна отримати доступ Foo.Bar:

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

У більшості випадків це не відрізняється від статичного внутрішнього класу.


35
Вкладений інтерфейс автоматично статичний, пише він ключове слово чи ні.
Paŭlo Ebermann

3
Нам дійсно потрібен спосіб для спільноти , щоб голосувати , щоб прийняти іншу відповідь: stackoverflow.com/a/74400/632951
Pacerier


@ ClintonN.Dreisbach Чи можете ви пояснити, що мається на увазі під собою The interface isn't associated with instances of the class, but with the class itselfдалі, я цього не зрозумів
Kasun Siyambalapitiya

6

Відповідь Джессі близька, але я думаю, що є кращий код, який продемонструє, чому внутрішній інтерфейс може бути корисним. Подивіться на код нижче, перш ніж читати далі. Чи можете ви знайти, чому внутрішній інтерфейс корисний? Відповідь полягає в тому, що клас DoSomethingAlready може бути примірник будь-якого класу, який реалізує A і C; не тільки конкретний клас Зоопарк. Звичайно, цього можна досягти, навіть якщо зміна струму не є внутрішнім, але уявіть, як об'єднати довші імена (не лише 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;
  }
}

2
Це нісенітниця. Клас Zooдійсно НЕ реалізує інтерфейс AC, тому випадки Zooне можуть бути передані в конструктор , DoSomethingAlreadyякий очікує AC. Те, що ACпоширює і те, Aі Cне означає, що класи, що реалізують Aі Cмагічно також реалізують AC.
Холгер

3

Щоб відповісти на ваше запитання дуже безпосередньо, подивіться на Map.Entry.

Map.Entry

також це може бути корисно

Запис в статичний вкладений інтерфейс


4
Це приклад, але насправді не відповідь.
Paŭlo Ebermann

Я використовую Map.Entry для створення об'єктів "Пара" багато. Це піддається. Реалізація пари має дві школи думок, але тут не сенс. Map.Entry може бути внутрішнім, але я використовую його зовні.
Равіндранат Акіла

0

Зазвичай я бачу статичні внутрішні класи. Статичні внутрішні класи не можуть посилатися на містять класи, де нестатичні класи можуть. Якщо ви не зіткнетеся з деякими зіткненнями пакетів (там вже є інтерфейс під назвою Bar у тому ж пакеті, що і Foo), я думаю, я б створив його власний файл. Також може бути дизайнерським рішенням застосувати логічний зв'язок між Foo та Bar. Можливо, автор задумав використовувати Бар лише з Foo (хоча статичний внутрішній інтерфейс цього не нав'яже, а лише логічне з'єднання)


"Статичний внутрішній" - це протиріччя в термінах. Вкладені класи бувають або статичними, або внутрішніми.
Маркіз Лорн

0

Якщо ви зміните клас Foo на інтерфейс Foo, ключове слово "public" у наведеному вище прикладі також буде зайвим.

Інтерфейс, визначений в іншому інтерфейсі, буде неявно публічним статичним.


Не відповів на питання
Дощ

0

У 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. Отже, всі інші відповіді правильні: немає різниці в статичних та нестатичних вкладених інтерфейсах.


Слід зазначити, що все це стосується GJ, який був дуже раннім процесором для генерики на Java.
Маркіз Лорн

-1

У Java статичний інтерфейс / клас дозволяє використовувати інтерфейс / клас, як клас вищого рівня, тобто він може бути оголошений іншими класами. Отже, ви можете:

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

Без статики вищезгадане не вдалося б скласти. Перевагою цього є те, що вам не потрібен новий вихідний файл лише для оголошення інтерфейсу. Він також візуально асоціює інтерфейс Bar із класом Foo, оскільки вам потрібно написати Foo.Bar, і це означає, що клас Foo робить щось із екземплярами Foo.Bar.

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


Без статичної вона робить компіляцію. "Статичний" є зайвим.
Маркіз Лорн

@EJP: Відповідно до цієї статті, до якої я посилаюся, без статики внутрішній клас може використовуватися тільки класом володіння, а не тим, що знаходиться поза класом заборгованості. Статика робить її вкладеним класом верхнього рівня і може бути використана об'єктами, що не входять до сфери володіння класом (принаймні, відповідно до статті). Статика не є надто вигідною і впливає на сферу внутрішнього класу. Це стосується класів, інтерфейси завжди можуть діяти як об'єкти верхнього рівня (для перевірки потрібно прочитати специфікацію мови). Можливо, я мав би розділити інтерфейс та частини класу своєї відповіді (моя погана).
Skizz

-6

Статичний означає, що будь-яка частина класу пакету (проекту) може отримати доступ до нього без використання покажчика. Це може бути корисним або перешкоджати залежно від ситуації.

Ідеальним прикладом корисних "статичних" методів є клас Math. Всі методи в математиці є статичними. Це означає, що вам не доведеться виходити зі свого шляху, робити новий примірник, оголошувати змінні та зберігати їх у ще більше змінних, ви можете просто ввести свої дані та отримати результат.

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

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


1
Я знаю, що означає статика. Я просто ніколи не бачив його в інтерфейсі раніше. Тому ваше запитання не по темі - вибачте
штат Міссурі

1
Я думаю, що його відповідь поза темою.
Корай Тугай
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.