Почему интерфейс для Java-класса должен быть предпочтительным? - PullRequest
69 голосов
/ 29 сентября 2008

PMD сообщит о нарушении для:

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

Нарушение было следующим: «Не используйте типы реализации, такие как ArrayList; используйте вместо этого интерфейс».

Следующая строка исправит нарушение:

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

Почему следует использовать последний с List вместо ArrayList?

Ответы [ 8 ]

75 голосов
/ 29 сентября 2008

Использование интерфейсов для конкретных типов - это ключ к хорошей инкапсуляции и слабой связи вашего кода.

Это даже хорошая идея следовать этой практике при написании собственных API. Если вы это сделаете, позже вы обнаружите, что проще добавить модульные тесты в ваш код (с использованием методов Mocking) и изменить базовую реализацию, если это потребуется в будущем.

Вот хорошая статья на эту тему.

Надеюсь, это поможет!

28 голосов
/ 29 сентября 2008

Это предпочтительнее, потому что вы отделяете свой код от реализации списка. Использование интерфейса позволяет легко изменить реализацию, в данном случае ArrayList, на другую реализацию списка, не меняя остальную часть кода, если он использует только методы, определенные в List.

12 голосов
/ 30 сентября 2008

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

Однако есть исключения, которые вы должны учитывать. Доступ к объектам через интерфейсы добавляет дополнительный уровень косвенности, который замедлит ваш код.

Для интереса я провел эксперимент, который сгенерировал десять миллиардов последовательных обращений к списку ArrayList длиной 1 миллион. На моем MacBook с частотой 2,4 ГГц доступ к ArrayList через интерфейс List в среднем занимал 2,10 секунды, а при объявлении его типа ArrayList - 1,67 секунды.

Если вы работаете с большими списками, глубоко внутри внутреннего цикла или часто вызываемой функцией, то это то, что нужно учитывать.

6 голосов
/ 29 сентября 2008

ArrayList и LinkedList являются двумя реализациями List, который представляет собой упорядоченную коллекцию элементов. С точки зрения логики не имеет значения, используете ли вы ArrayList или LinkedList, поэтому вам не следует ограничивать тип этим.

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

2 голосов
/ 27 декабря 2016

Почему последний с 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).*/

OR

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.*/

OR

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

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

OR

некоторая другая List конкретная реализация.

List интерфейс определяет контракт, и конкретная реализация List может быть изменена. Таким образом, интерфейс и реализация слабо связаны.

Вопрос по теме SE:

Что значит «программировать на интерфейс»?

1 голос
/ 04 октября 2008

Даже для локальных переменных помогает использование интерфейса поверх конкретного класса. Вы можете в конечном итоге вызвать метод, который находится за пределами интерфейса, и тогда будет сложно изменить реализацию списка, если это необходимо. Кроме того, лучше всего использовать наименее определенный класс или интерфейс в объявлении. Если порядок элементов не имеет значения, используйте коллекцию вместо списка. Это дает вашему коду максимальную гибкость.

1 голос
/ 30 сентября 2008

Свойства ваших классов / интерфейсов должны предоставляться через интерфейсы, потому что это дает вашим классам контракт на использование, независимо от реализации.

Однако ...

В объявлениях локальных переменных делать это бессмысленно:

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

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

1 голос
/ 29 сентября 2008

В целом, для вашей строки кода не имеет смысла беспокоиться об интерфейсах. Но, если мы говорим об API, есть действительно веская причина. Я получил маленький класс

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

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

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...