Чому слід віддавати перевагу інтерфейсу для класу Java?


75

PMD повідомляє про порушення щодо:

ArrayList<Object> list = new ArrayList<Object>();

Порушення було "Уникайте використання типів реалізації, таких як 'ArrayList'; замість цього використовуйте інтерфейс".

Наступний рядок виправить порушення:

List<Object> list = new ArrayList<Object>();

Чому останній Listслід використовувати замість ArrayList?


Відповіді:


80

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

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

Ось гарна стаття на цю тему.

Сподіваюся, це допоможе!


1
А як щодо використання "Колекція" замість "Список", тож ми йдемо на крок далі в абстракції ...?
user1156544

31

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


14

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

Однак є винятки, які ви повинні врахувати. Доступ до об’єктів через інтерфейси додає додатковий рівень опосередкованості, який робить ваш код повільнішим.

Для інтересу я провів експеримент, який дав десять мільярдів послідовних звернень до ArrayList довжиною 1 мільйон. На моєму MacBook 2,4 ГГц доступ до ArrayList через інтерфейс List зайняв у середньому 2,10 секунди, при оголошенні типу ArrayList - в середньому 1,67 секунди.

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


@Owen: +5 Проникливий повтор: Різниця в продуктивності ... Дуже несподівано
Ен Тернер

2
Відповідь на це питання , однак , показує , що накладні витрати можуть бути дуже малі: stackoverflow.com/questions/890687 / ...
Raedwald

1
Оце Так! 0,5 секунди за десять мільярдів доступу, тобто 1 доступ до інтерфейсу на половину наносекунди повільніший, ніж доступ до класу! Це, звичайно, причина ніколи, ніколи не використовувати інтерфейси.
Інго

6

ArrayList та LinkedList - це дві реалізації списку, який є упорядкованою колекцією елементів. За логікою неважливо, використовуєте ви ArrayList або LinkedList, тому ви не повинні обмежувати тип таким.

Це контрастує з висловом "Колекція" та "Список", які є різними речами (Список передбачає сортування, Колекція - ні).


2

Чому останній із List слід використовувати замість ArrayList?

Це хороша практика: програма інтерфейсу, а не реалізації

Шляхом заміни ArrayList на List, ви можете змінити Listреалізацію в майбутньому, як показано нижче, залежно від випадку використання бізнесу.

List<Object> list = new  LinkedList<Object>(); 
/* Doubly-linked list implementation of the List and Deque interfaces. 
 Implements all optional list operations, and permits all elements (including null).*/

АБО

List<Object> list = new  CopyOnWriteArrayList<Object>(); 
/* A thread-safe variant of ArrayList in which all mutative operations
 (add, set, and so on) are implemented by making a fresh copy of the underlying array.*/

АБО

List<Object> list = new  Stack<Object>(); 

/* The Stack class represents a last-in-first-out (LIFO) stack of objects.*/

АБО

якийсь інший List конкретні реалізації.

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

Пов’язане питання SE:

Що означає "програмувати на інтерфейс"?


А як щодо використання "Колекція" замість "Список", тож можна використовувати Список або Набір, наприклад
user1156544

1

Навіть для локальних змінних допомагає використання інтерфейсу над конкретним класом. У підсумку ви можете викликати метод, який знаходиться поза інтерфейсом, і тоді важко змінити реалізацію Списку, якщо це необхідно. Крім того, найкраще використовувати найменш конкретний клас або інтерфейс у декларації. Якщо порядок елементів не має значення, використовуйте Collection замість List. Це надає коду максимальну гнучкість.


1

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

Однак ...

У оголошеннях локальних змінних мало сенсу робити це:

public void someMethod() {
List theList = new ArrayList();
//do stuff with the list
}

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


1

Загалом для вашого рядка коду не має сенсу турбуватися про інтерфейси. Але якщо ми говоримо про API, є дійсно вагома причина. Я отримав невеликий клас

class Counter {
    static int sizeOf(List<?> items) {
        return items.size();
    }
}

У цьому випадку потрібне використання інтерфейсу. Тому що я хочу підрахувати розмір усіх можливих реалізацій, включаючи власні замовлення. class MyList extends AbstractList<String>....


Якщо ви кодуєте такі реалізації, як ArrayList, є всі можливості, що ви можете використовувати методи реалізації, навіть коли ви можете зробити те саме з методами інтерфейсу. Це прив’яже ваш код до реалізації, що ускладнить перемикання між реалізаціями пізніше.
Чемпіон

0

Інтерфейс доступний кінцевому користувачеві. Один клас може реалізувати кілька інтерфейсів. Користувач, який має доступ до певного інтерфейсу, має доступ до певної поведінки, яка визначена в цьому конкретному інтерфейсі.

Один інтерфейс також має кілька варіантів реалізації. На основі сценарію система буде працювати з різними сценаріями (Впровадження інтерфейсу).

дайте мені знати, якщо вам потрібні додаткові пояснення.


0

Інтерфейс часто краще представлений у поданні налагоджувача, ніж конкретний клас.

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