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

Кто участвовал в декабрьском адвенте, точно помнит, что еnum компилируется в наследник класса Enum:

public enum Animal {WOLF, TIGER}
↓
public class Animal extends Enum {
public static final Animal WOLF;
public static final Animal TIGER;
}
Подробнее об этом и енамах в целом можно почитать тут — раз, два и три.

В определении класса Enum используется конструкция, которая называется self-referential generic (или self-bound type, или recursive generic):

EnumᐸE extends EnumᐸEᐳᐳ

В этом посте расскажу, что это такое и зачем нужно.

Чтобы понять, какая проблема решается, представим, что этой конструкции нет. И определение енама выглядит так:

public abstract class MyEnum implements ComparableᐸMyEnumᐳ

Пользователь определяет enum Animal и enum Converter. Компилятор превращает это в классы

Animal extends MyEnum
Converter extends MyEnum

Каждый класс должен реализовать интерфейс ComparableᐸMyEnumᐳ и метод compareTo. Чтобы не сравнивать животных и конвертеры, придётся использовать instanceof:

public final int compareTo(MyEnum o) {
if (o instanceOf Animal other) {
// сравниваем зверюшек
// return ...
}
throw IllegalArgumentException();
}

В самом instanceOf нет ничего плохого. Тем более этот код генерируется при компиляции и остаётся за кадром.

Есть более важный момент. Пользователь может спокойно сравнить животное и конвертер, ошибка возникнет только в рантайме. Это выглядит странно, ведь enum Animal и enum Converter никак не связаны между собой.

Здесь дженерик выходит на сцену:

public abstract class EnumᐸE extends EnumᐸEᐳᐳ implements ComparableᐸEᐳ

🔸 Добавляем параметр E, совместимый с классом Enum
🔸 Используем E в интерфейсе Comparable
🔸 Компилируем enum Animal в
public class Animal extends EnumᐸAnimalᐳ
🔸 Теперь Comparable использует тип Animal, и метод compareTo станет таким:
public int compareTo(Animal o)

✅ Убрали instanceOf, код стал меньше и быстрее
✅ При компиляции происходит проверка типов:

Animal zebra = Animal.ZEBRA;
Converter csv = Converter.CSV;
❌ zebra.compareTo(csv); // не скомпилируется!

Self-referential generic позволяет использовать дочерний тип в интерфейсах и методах родителя. Для некоторых кейсов этот приём здорово упрощает код и снижает количество ошибок. В следующем посте покажу ещё один пример использования.

Ответ на вопрос перед постом: self-referential generic помогает ограничить сравнение разных enum между собой.
  • 🔥 91
  • 👍 29
  • ❤ 13
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 →