LinkedHashMap: наследование и композиция
Сегодня на примере LinkedHashMap очень чётко покажу проблему наследования. Ни в одной теоретической статье не найдёте такого наглядного примера. Просто бриллиант💎
Действующие лица - классы HashSet, HashMap, LinkedHashSet и LinkedHashMap. В них много общего кода, и организовать нужные связи — задача со звёздочкой.
В JDK эту задачу решили не лучшим образом. Как раз по вине наследования.
Вернёмся в 1998 год. Я смотрела Сейлор Мун, кто-то из подписчиков даже не родился. Ещё в тот год вышла java 1.2. В пакете java.util были 2 всем знакомых класса: HashMap и HashSet.
HashSet сделан на базе HashMap примерно так:
public class HashSet implements Set {
HashMap map;
public HashSet() {
map = new HashMap();
}
...
}
Прошло 4 года. В java 1.4 добавились Linked* реализации:
▫️ LinkedHashMap стал наследником HashMap. Тут всё сложилось удачно
▫️ LinkedHashSet стал наследником HashSet. И здесь не всё гладко.
У HashSet внутри HashMap, доступа снаружи к нему нет. А внутри LinkedHashSet должен быть LinkedHashMap. Как заменить объект внутри родителя без изменения существующих методов?
Решение в итоге ужасное. В HashSet добавили такой package private конструктор:
HashSet(int initialCapacity, float loadFactor, boolean dummy) {
map = new LinkedHashMap(initialCapacity, loadFactor);
}
поле dummy нужно, чтобы этот конструктор отличался от уже существующих.
Только вдумайтесь: в родителя добавили специальный конструктор для конкретного потомка. С его деталями реализации!
Код получился бы чище, если вместо наследования от хэшсета использовать композицию:
class LinkedHashSet implements Set {
private LinkedHashMap map;
public LinkedHashSet() {
this.map = new LinkedHashMap();
}
...
}
✅ В HashSet нет лишнего
😑 Нужно скопировать кучу мелких методов типа add, size, contains
На примере LinkedHashSet чётко видна проблема наследования.
У родителя по всем заветам инкапсуляции скрыта реализация. Если наследование изначально не предусмотрено, к внутренним полям родителя нет прямого доступа. Код сложнее переиспользовать, и появляются странные конструкции.
Даже если дети спланированы заранее, зависимость от родителей ограничивает их развитие. Детям приходится подстраиваться и выбирать не самые оптимальные для себя решения.
(этот абзац всё ещё про разработку)
Композиция и в моменте, и в перспективе гораздо удобнее. Но 30 лет назад это было не так очевидно.