Чи є "статичний інтерфейс" хорошою практикою?


13

Нещодавно я помітив, що в інтерфейсах є статичні методи. Як і у статичних полів інтерфейсу, є цікава поведінка: вони не передаються у спадок.

Я не впевнений, що корисне у фактичних інтерфейсах, які потрібно реалізувати. Однак це дозволяє програмісту створювати інтерфейси, які є просто конвертами для статичних матеріалів, як, наприклад, класи класів.

Простий приклад - просто конверт для глобальних констант. Порівняно з класом, ви можете легко помітити відсутню котельну табличку, public static finalяк передбачається (що робить її менш багатослівною).

public interface Constants {
    String LOG_MESSAGE_FAILURE = "I took an arrow to the knee.";
    int DEFAULT_TIMEOUT_MS = 30;
}

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

public interface ConfigKeys {
    static createValues(ConfigKey<?>... values) {
        return Collections.unmodifiableSet(new HashSet(Arrays.asList(values)));
    }

    static ConfigKey<T> key(Class<T> clazz) {
        return new ConfigKey<>(clazz);
    }

    class ConfigKey<T> {
        private final Class<T> type;
        private ConfigKey(Class<T> type) {
            this.type = type;
        }
        private Class<T> getType() {
            return type;
        }
    }
}

import static ConfigKeys.*;
public interface MyAppConfigKeys {
    ConfigKey<Boolean> TEST_MODE = key(Boolean.class);
    ConfigKey<String> COMPANY_NAME = key(String.class);

    Set<ConfigKey<?>> VALUES = createValues(TEST_MODE, COMPANY_VALUE);

    static values() {
        return VALUES;
    }
}

Ви також можете створити якийсь корисний "клас" таким чином. Однак у комунальних службах часто корисно використовувати приватні або захищені допоміжні методи, що на заняттях не зовсім можливо.

Я вважаю це приємною новою функцією, і особливо те, що статичні члени не успадковуються - цікава концепція, яка була введена лише для інтерфейсів.

Цікаво, чи можете ви вважати це гарною практикою. Хоча стиль коду та передовий досвід не є аксіоматичними і є місце для думки, я думаю, що зазвичай існують вагомі причини, які підтверджують цю думку.

Мене більше цікавлять причини (не) використання таких моделей.


Зауважте, що я не збираюся реалізовувати ці інтерфейси. Вони просто конверт для їх статичного змісту. Я маю намір використовувати лише константи або методи і, можливо, статичний імпорт.


1
Реакція мого коліна на це полягає в тому, що це може призвести лише до поганих речей. Навіть незважаючи на те, що загальнодоступні статичні методи по суті є просто просторовими глобальними функціями. Схоже, що дозвол будь-якого коду реалізації в Інтерфейсі суперечить своїй меті, оскільки визначення договору відсутнє. Я кажу, залиште часткову реалізацію заняттям «Анотація» Мені потрібно буде прочитати, чому це було додано.
Адріан

Я маю йти зараз, але завтра напишу щось на цьому. Недостатнє значення полягає в тому, що раніше Інтерфейс мав чітку мету, яка була чітко відокремлена від мети абстрактного класу. Тепер вони сильно перекриваються таким чином, що порушує мовне агностичне визначення інтерфейсу. Крім того, є випадки, коли ви не можете успадковувати з декількох інтерфейсів більше, якщо вони реалізують методи за замовчуванням. Тож тепер є винятки з багатонаступного успадкування інтерфейсів, де раніше цього не було. Оскільки Java різко відхилилася від загального визначення інтерфейсу ...
Адріан

це також може заплутати нових розробників, які намагаються отримати належну обробку щодо основних концепцій OOP, таких як Interfaces, AbstractClases, коли використовувати композицію замість успадкування, ... тощо.
Адріан

Aliester, ти говориш про defaultметоди. Я говорю про staticметоди та поля. Вони не передаються у спадок, тому вони не порушують багаторазового успадкування.
Власек

Відповіді:


1

Простий приклад - просто конверт для глобальних констант.

Розміщення констант на інтерфейсах називається, Constant Interface Antipatternоскільки константи часто є лише деталі реалізації. Подальші недоліки узагальнені в статті Вікіпедії про постійний інтерфейс .

Ви також можете створити якийсь корисний "клас" таким чином.

Дійсно, документ Java заявляє, що статичні методи можуть полегшити організацію допоміжних методів, наприклад, коли викликати допоміжні методи з методів за замовчуванням.


1
Я не думаю, що це насправді є відповіддю на поставлене запитання, оскільки ви можете зробити обидва ці речі без цього нового функціоналу.
Адріан

Так, згідно з документацією, він призначений як помічний метод. Однак це також публічне, що йому суперечить. Тож, можливо, він також покликаний допомогти класам, які реалізують інтерфейс. Але вони не передаються у спадок, тож вам доведеться використовувати статичний імпорт, щоб відчути, що ви його успадкували. Ну, дякую, що подивився на це в документах.
Власек

2
Немає нічого поганого в тому, щоб вставити константи в інтерфейси, якщо ці константи якимось чином є частиною API, описаного інтерфейсом. Єдине, що є антипатернним - це введення констант в інтерфейси без методів, а класи мають реалізувати інтерфейс лише для позначення констант без префіксу класу. Це зловживання інтерфейсами, а з моменту введення статичного імпорту повністю застаріло.
Майкл Боргвардт

1

Як зазначено в запитанні, інтерфейс не призначений для впровадження (або примірника). Тож ми могли б порівняти його з класом, який має приватний конструктор:

  • Клас не можна розширювати без super()дзвінка, але інтерфейс можна "реалізувати"
  • Клас не може бути ініційований без роздумів, тоді як інтерфейс можна легко інстанціювати як анонімний клас, простий як цей: new MyInterface() {};

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

Існує також дивна річ щодо спадкування з "статичного інтерфейсу":

  • Клас "реалізація" успадковує поля, але не успадковує статичні методи
  • Розширюваний інтерфейс не успадковує статичних членів взагалі
  • (Хоча клас, що розширює клас, успадковує кожного не приватного члена ... і оскільки його не можна розширити інтерфейсом, є менше місця для плутанини)

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


... У будь-якому випадку, умови кодування команди можуть охопити ці проблеми. Для мене, хоча він не пов’язує руку програміста майже так сильно, як клас з приватним конструктором, він все ще несе менші ризики, ніж сетери, і сетери часто використовуються.
Власек

0

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

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


Так, статичний імпорт здається кращою ідеєю, ніж успадкування констант, і він працює з статичними членами класів і інтерфейсів. І це видно лише всередині класу / інтерфейсу.
Власек

@Vlasec Правильно, але немає необхідності робити це в інтерфейсі. Єдина відмінність робити це з класом та інтерфейсом полягає в тому, що ви можете успадкувати більше ніж один інтерфейс. Стандартна практика для класу корисних програм, як це, полягає в тому, щоб зробити конструктор приватним для запобігання інстанції або розширення. Немає користі робити це в інтерфейсі над класом. Він просто відкриває двері для нецільового використання.
JimmyJames

Я бачу значення в константах на інтерфейсах. Інтерфейси, як правило, визначають методи і методи, іноді приймають константи як параметри. Наприклад: interface ColorFilter {Image applyGradient(int colorSpace, int width, byte[] pixels); }. Я можу собі уявити , щоб там бути деякі різні константи RGB, RGBA, CMYK, GREYSCALE, і INDEXEDт.д.
Стейн де Вітт

@StijndeWitt Це було б не найгірше, але для цього можна використовувати клас enum або вкладений клас в інтерфейсі. Справжня проблема полягає у використанні цього для залучення статики до сфери багатьох класів шляхом успадкування. Цей підхід явно поступається використанню статичного імпорту.
JimmyJames

0

Я б сказав, якщо вас цікавить, як правильно використовувати нову функцію, погляньте на код у JDK. Методи за замовчуванням на інтерфейсах дуже широко використовуються і їх легко знайти, але статичні методи на інтерфейсах здаються дуже рідкісними. Деякі приклади включають в себе java.time.chrono.Chronology, java.util.Comparator, java.util.Mapі досить багато інтерфейсів в java.util.functionі java.util.stream.

Більшість прикладів, які я розглядав, стосуються використання тих API, які, ймовірно, є дуже поширеними: перетворення між різними типами в API часу, наприклад, для порівнянь, які, ймовірно, часто робляться між об'єктами, переданими функціям або об'єктам, що містяться в колекції. Мій винос: якщо є частина вашого API, яка повинна бути гнучкою, але ви можете покрити, наприклад, 80% випадків використання кількома налаштуваннями, подумайте про використання статичних методів на своїх інтерфейсах. З огляду на те, як нечасто ця функція здається використаною в JDK, я б дуже консервативно використовував її у власному коді.

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


-1

Цікаво, чи є причини не використовувати його.

Як щодо, гм, сумісності зі старими спільними машинами?

Ви вважаєте це загалом гарною ідеєю?

Ні. Це зворотно-несумісна зміна, яка додає зухвалості виразності мови. Два великі пальці вниз.

Чи є якісь модельні ситуації, коли ви настійно рекомендуєте заняття?

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

ОНОВЛЕННЯ: Будь ласка, більше жодної трансляції Очевидно, що деякі люди думають, що статичні методи в інтерфейсах - це коліни бджіл, і я цим капітулюю. Я би щойно видалив цю відповідь, але коментарі, що додаються до неї, цікаві для обговорення.


Коментарі не для розширеного обговорення; ця розмова була переміщена до чату .
maple_shaft

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