TGViewer
Java: fill the gaps Java: fill the gaps @java_fillthegaps · 12.3K subscribers
Post #635 10K
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 лет назад это было не так очевидно.
  • 🔥 125
  • 👍 50
  • ❤ 25
  • 👎 1
More from @java_fillthegaps
  1. Apr 30, 2026Get Your Hands Dirty on Clean Architecture: отзыв на книгу Когда я подняла тему чистой арх…
  2. Apr 27, 2026​Clean Architecture: отзыв на книгу Наконец-то дочитала книгу Clean Architecture Роберта М…
  3. Mar 4, 2026Как переиспользовать контекст в интеграционных тестах Сегодня расскажу базовый минимум для…
  4. Mar 4, 2026Post #660
  5. Mar 4, 2026Тестовый контекст поднимается 2 минуты. У нас 4 класса с интеграционными тестами, их конфи…
  6. Feb 25, 2026Чистая архитектура. Главы 3-5 Продолжаем спидран по Clean Architecture Роберта Мартина. ⭐️…
Threads Profile ViewerView any public Threads profile without an account.Open ThreadLook →Writing with AI? Make it sound human.Metric37 rewrites AI drafts so they read naturally. Free AI detector, 1,500 words free.Try Metric37 →