Полиморфизм: зачем использовать "List list = new ArrayList" вместо "ArrayList list = new ArrayList"?

Возможный дубликат:
Зачем нужен интерфейс для класса Java?

Когда мне следует использовать

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

ArrayList наследуется от List, поэтому, если некоторые функции в ArrayList не находятся в List, то я потеряю некоторые функции ArrayList, правильно? И компилятор заметит ошибку при попытке доступа к этим методам?

Ответ 1

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

List list = new ArrayList();  

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

Например, скажем, что вы пишете довольно большую стороннюю библиотеку и говорите, что решили реализовать ядро ​​своей библиотеки с помощью LinkedList. Если ваша библиотека в значительной степени зависит от доступа к элементам в этих списках, то в конечном итоге вы обнаружите, что вы сделали плохое дизайнерское решение; вы поймете, что вы должны использовать ArrayList (который дает время доступа O (1)) вместо LinkedList (что дает время доступа O (n)). Предполагая, что вы программируете на интерфейс, такое изменение легко. Вы просто изменили бы экземпляр List from,

List list = new LinkedList();

к

List list = new ArrayList();  

и вы знаете, что это будет работать, потому что вы написали свой код, чтобы следовать контракту, предоставленному интерфейсом List.

С другой стороны, если вы внедрили ядро ​​своей библиотеки с помощью LinkedList list = new LinkedList(), такое изменение было бы не таким простым, поскольку нет гарантии, что остальная часть вашего кода не будет использовать методы, специфичные для класса LinkedList.

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

Ответ 2

Это называется программированием для интерфейса. Это будет полезно в случае, если вы захотите перейти к другой реализации List в будущем. Если вам нужны какие-то методы в ArrayList, тогда вам нужно будет запрограммировать реализацию ArrayList a = new ArrayList().

Ответ 3

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

public ArrayList getList();

Затем вы решите изменить его,

public LinkedList getList();

Любой, кто выполнял ArrayList list = yourClass.getList(), должен будет изменить свой код. С другой стороны, если вы это сделаете,

public List getList();

Изменение реализации ничего не меняет для пользователей вашего API.

Ответ 4

Я думаю, что ответ @tsatiz в основном прав (программирование на интерфейс, а не на реализацию). Однако , программируя интерфейс, вы не потеряете никаких функций. Позволь мне объяснить.

Если вы объявляете свою переменную как List<type> list = new ArrayList<type>, вы фактически не теряете функциональности ArrayList. Все, что вам нужно сделать, это сделать list до ArrayList. Вот пример:

List<String> list = new ArrayList<String>();
((ArrayList<String>) list).ensureCapacity(19);

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

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

Ответ 5

Это позволяет вам написать что-то вроде:

void doSomething() {
    List<String>list = new ArrayList<String>();
    //do something
}

Позже вы можете изменить его на:

void doSomething() {
    List<String>list = new LinkedList<String>();
    //do something
}

не изменяя остальную часть метода.

Однако, если вы хотите использовать CopyOnWriteArrayList, например, вам нужно объявить его как таковой, а не как List, если вы хотите использовать его дополнительные методы (например, addIfAbsent):

void doSomething() {
    CopyOnWriteArrayList<String>list = new CopyOnWriteArrayList<String>();
    //do something, for example:
    list.addIfAbsent("abc");
}

Ответ 6

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

Более того, вы используете эту конструкцию, когда считаете, что можете изменить реализацию списка, который используете. Скажем, вы использовали конструкцию с ArrayList, и ваша проблема не была потокобезопасной. Теперь вы хотите сделать его потокобезопасным, и для части вашего решения вы, например, измените использование вектора. Что касается других видов использования этого списка, не имеет значения, будет ли он AraryList или Vector, просто списком, никаких новых изменений не потребуется.

Ответ 7

Я полагаю, что суть вашего вопроса заключается в следующем: program to an interface, not to an implementation

Просто потому, что интерфейс дает вам больше абстракции и делает код более гибкими и устойчивыми к изменениям, поскольку вы можете использовать разные реализаций одного и того же интерфейса (в этом случае вы можете захотеть изменить реализацию списка на связанныйList вместо ArrayList), не меняя своего клиента.

Ответ 8

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

Однако существуют определенные ситуации, когда вы предпочитаете использовать конкретную реализацию. Например, при сериализации в GWT.