Ответ 1
Ничто в этом ответе не позволяет расширить 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
аннотация, чтобы сделать то же самое. Я не уверен, как Ломбок выведет интерфейс, или если он это сделает.
Я с glowcoder, я не вижу ничего принципиально неправильного с расширением в этом случае - это действительно вопрос, какое решение лучше подходит для проблемы.
Изменить для получения информации о том, как наследование может нарушать инкапсуляцию
Подробнее см. Bloch Effective 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 суперкласса, чтобы определить, когда и если, подкласс должен измениться.