Ничто в этом ответе не препятствует расширению ArrayList;была проблема с синтаксисом.Расширение класса существует, поэтому мы можем повторно использовать код.
Обычные возражения против расширения класса - это обсуждение "композиция предпочтений по наследованию" .Расширение не всегда является предпочтительным механизмом, но оно зависит от того, что вы на самом деле делаете.
Правка для примера композиции по запросу.
public class ThingContainer implements List<Thing> { // Or Collection based on your needs.
List<Thing> things;
public boolean add(Thing thing) { things.add(thing); }
public void clear() { things.clear(); }
public Iterator<Thing> iterator() { things.iterator(); }
// Etc., and create the list in the constructor
}
Вы бы не хотели необязательно , чтобы предоставить полный интерфейс списка, просто коллекцию или ее вообще нет.Предоставление ни одной из функциональных возможностей значительно снижает общую полезность, хотя.
В Groovy вы можете просто использовать аннотацию @Delegate
для автоматического создания методов.Java может использовать аннотацию Project Lombok @Delegate
, чтобы сделать то же самое.Я не уверен, как Lombok выставит интерфейс, или если он это сделает.
Я использую свечение, я не вижу в этом ничего принципиально неправильного с расширением - это действительно вопрос того,Решение лучше подходит для проблемы.
Отредактируйте для получения подробных сведений о том, как наследование может нарушать инкапсуляцию
Подробнее см. Эффективную Java Блоха, пункт 16.
Если подкласс полагается на поведение суперкласса, и поведение суперкласса изменяется, подкласс может сломаться.Если мы не контролируем суперкласс, это может быть плохо.
Вот конкретный пример, взятый из книги (извините, Джош!), В псевдокоде и перефразированном (все ошибки мои).
class CountingHashSet extends HashSet {
private int count = 0;
boolean add(Object o) {
count++;
return super.add(o);
}
boolean addAll(Collection c) {
count += c.size();
return super.addAll(c);
}
int getCount() { return count; }
}
Тогда мы используем это:
s = new CountingHashSet();
s.addAll(Arrays.asList("bar", "baz", "plugh");
И оно возвращает ... три?Нету.Шесть.Почему?
HashSet.addAll()
реализовано на HashSet.add()
, но это внутренняя деталь реализации.Наш подкласс addAll()
добавляет три вызова super.addAll()
, который вызывает add()
, что также увеличивает счетчик.
Мы могли бы удалить addAll()
подкласса, но теперь мы полагаемся на детали реализации суперкласса,который может измениться.Мы могли бы изменить наш addAll()
, чтобы выполнять итерацию и вызывать add()
для каждого элемента, но теперь мы переопределяем поведение суперкласса, которое противоречит цели и не всегда возможно, если поведение суперкласса зависит от доступа к закрытым членам.
Или суперкласс может реализовать новый метод, которого нет у нашего подкласса, что означает, что пользователь нашего класса может непреднамеренно обойти предполагаемое поведение, напрямую вызвав метод суперкласса, поэтому мы должны отслеживать API суперкласса, чтобы определить, когда,и если подкласс должен измениться.