TGViewer
Java: fill the gaps Java: fill the gaps @java_fillthegaps · 12.3K subscribers
Post #502 8.33K
Self-referential generic, часть 2

В прошлом посте мы выяснили, зачем self-referential generic нужен в Enum: для использования дочернего типа при реализации интерфейса родителя. Это довольно экзотичный кейс. Сегодня покажу более практичный пример, как дженерики облегчили работу с иерархией и неизменяемыми переменными.

Дано: класс Delivery с информацией о доставке. Метод cancelled делает заказ недействительным. У класса есть наследник FastDelivery, в котором дополнительно хранится ID курьера:

class Delivery {
final long id;
final boolean isActive;

public Delivery(long id, boolean isActive) {…}

public Delivery cancelled() {
return new Delivery(this.id, false);
}
}

class FastDelivery extends Delivery {
private final long courierId;

public FastDelivery(…) {…}

public long getCourierId() {
return courierId;
}
}

Проблема: метод cancelled возвращает объект типа Delivery, и мы теряем информацию о курьере:

FastDelivery fast = new FastDelivery(…);
Delivery cancelled = fast.cancelled();

❌ long id = fast.getCourierId();

В такой ситуации помогут self-referential generic и небольшой обходной манёвр:

🔸 Добавляем параметр в родителя
public class DeliveryᐸT extends DeliveryᐸTᐳᐳ

🔸 Создаём метод create, который возвращает нужный экземпляр
protected T create(long id, boolean isActive) {
return (T) new Delivery(id, isActive);
}

🔸 Используем этот метод в cancelled
public T cancelled() {
return create(this.id, false);
}

🔸 Определяем параметр в наследнике
public class FastDelivery extends DeliveryᐸFastDeliveryᐳ

🔸 Переопределяем метод create в наследнике
protected FastDelivery create(long id, boolean isActive) {
return new FastDelivery(this.id, this.isActive, courierId);
}

Всё! Теперь информация не теряется:
FastDelivery fast = new FastDelivery(…);
FastDelivery cancelled = fast.cancelled();

✅ long id = cancelled.getCourierId();

Здесь используется комбо двух приёмов:

🔹 Метод create и его переопределение позволяют использовать поля, доступные в наследнике и вернуть нужный объект
🔹 Self-referential generic помогает вернуть нужный тип в методе cancelled

Готовый код доступен здесь

Резюме

Рассмотрите использование self-referential generic, когда

▫️ У вас есть иерархия
▫️ Родительский тип упоминается в аргументах или возвращаемом значении

Дополнительная типизация снизит количество кода, вытащит ошибки на этап компиляции и для некоторых случаев окажется очень изящным решением✨
  • 🔥 85
  • 👍 28
  • ❤ 12
  • 👎 2
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 →