Вариант 1 считается программированием для интерфейса , где вариант 2 - для программирования реализации . Последнее иногда необходимо, но первое дает вам возможность легко переключать реализации, гарантируя, что вы не зависите от методов, предоставляемых конкретной реализацией.
Кроме того, если вы создаете методы, которым требуется только функциональность, предоставляемая интерфейсом, то они должны быть объявлены как требующие интерфейса, чтобы любой объект, реализующий интерфейс, мог быть передан им. Это расширяет возможности повторного использования API. Например:
// This can be called passing any List
public int countItems(List lst, Filter flt) {
// iterate list, apply filter, and count matching objects
}
// This can called passing only an ArrayList, an unnecessary limitation in this case
public int countItems(ArrayList lst, Filter flt) {
// iterate list, apply filter, and count matching objects
}
Тем не менее, для некоторых интерфейсов существуют скрытые ловушки, зависящие от реализации (по крайней мере, в Java). Пример этого в List.get(int)
; если у вас есть ArrayList
, это эффективно, но для LinkedList
это не так. Если список очень большой, разница может быть существенной, особенно для плохо продуманной конструкции, такой как этот цикл:
for(int xa=0,len=list.length; xa<len; xa++) {
Object obj=list.get(xa);
obj.doSomething();
}
, который имеет ужасную производительность для больших связанных списков, так как список должен проходиться с начала для каждого get(xa)
.