Получить доступ

Дженерики: массивы, varargs и bridge-методы

Почему дженерики не дружат с массивами, heap pollution и @SafeVarargs, синтетические bridge-методы и reifiable-типы.

Дженерики и массивы не дружат

Массивы reifiable (знают свой тип элемента в рантайме) и ковариантны; дженерики стёрты и инвариантны. Эти модели несовместимы, поэтому нельзя создать массив параметризованного типа: new List<String>[10] не компилируется, как и new T[n] внутри дженерик-кода.

Запрет не каприз: будь это разрешено, ковариантность массивов плюс стирание дали бы heap pollution — массив принял бы элемент неверного типа без ArrayStoreException (тип-то стёрт), и ClassCastException вылез бы позже в неожиданном месте.

Как обойти

Предпочтительно — использовать List<T> вместо массива дженериков: это и есть штатное решение. Если массив действительно нужен (например, в реализации коллекции), его создают через рефлексию Array.newInstance(componentType, n) с Class-токеном и приводят, локально подавляя предупреждение.

@SuppressWarnings("unchecked") ставят максимально узко (на переменную/выражение, а не на метод) и только когда вручную доказана типобезопасность — это осознанное «беру ответственность», а не способ заглушить варнинги.

@SuppressWarnings("unchecked")
T[] arr = (T[]) Array.newInstance(componentType, n); // componentType: Class<T>

Heap pollution и varargs

Heap pollution — ситуация, когда переменная параметризованного типа ссылается на объект не того типа; компилятор предупреждает unchecked-варнингом. Частый источник — generic varargs: T... под капотом создаёт массив T[], а массив дженериков, как мы выяснили, небезопасен.

Если ваш generic-varargs-метод только ЧИТАЕТ элементы и не хранит/не публикует сам массив наружу, он безопасен — это помечают аннотацией @SafeVarargs, которая убирает предупреждение у вызывающих. Ставить её на небезопасный метод (который кладёт в массив или возвращает его) — ошибка.

Bridge-методы

Bridge-метод — синтетический метод, который компилятор генерирует, чтобы полиморфизм продолжал работать после стирания. Когда вы реализуете обобщённый интерфейс конкретным типом, стёртая сигнатура метода надкласса (например, compare(Object,Object)) расходится с вашей (compare(String,String)). Чтобы переопределение действительно срабатывало через ссылку на интерфейс, компилятор добавляет мост compare(Object,Object), который кастит и вызывает вашу версию.

Видеть их полезно: bridge-методы помечены isBridge()/isSynthetic() в рефлексии и иногда всплывают в стектрейсах — это не ваш код, а сгенерированный компилятором мостик.

class StrCmp implements Comparator<String> {
    public int compare(String a, String b) { return a.length() - b.length(); }
    // компилятор добавит синтетический bridge:
    // public int compare(Object a, Object b) { return compare((String) a, (String) b); }
}

Reifiable и non-reifiable типы

Reifiable тип полностью представлен в рантайме: примитивы, обычные (не-параметризованные) классы, raw types и wildcard вида List<?>. Non-reifiable — параметризованные типы вроде List<String>, чья информация стёрта.

Отсюда правило: instanceof и создание массива работают только с reifiable-типами (obj instanceof List<?> можно, instanceof List<String> — нет), а приведение к non-reifiable типу даёт unchecked-предупреждение, потому что в рантайме его проверить невозможно.

Как отвечать на собеседовании

Объясните конфликт «массивы reifiable+ковариантны / дженерики стёрты+инвариантны» и почему запрещён new T[] (heap pollution). Расскажите про generic varargs и роль @SafeVarargs (метод только читает). Бонус-уровень — что такое bridge-методы (синтетический мост для полиморфизма после стирания) и reifiable vs non-reifiable (instanceof только с reifiable).

Вопросы по теме

Читайте также

Закрытая бета

Знать тему — половина дела

На живом собеседовании sotto слышит вопрос интервьюера и показывает короткий ответ и план. Оставьте контакт — пришлём доступ и промокод.

  • Промокод на 2 часа — сверх пробных 30 минут
  • Программа для Windows — скачать сразу, войти — через Яндекс ID или VK ID
  • Прямая связь с разработчиком: что улучшить — сделаем