Преобразование List<SubClass> в List<BaseClass> эффективно
Пройдите тест, узнайте какой профессии подходите
Быстрый ответ
Для эффективного и безопасного преобразования List<Подкласс>
в List<Базовый класс>
рекомендуется использовать wildcard:
List<Подкласс> подклассы = new ArrayList<>();
List<? extends БазовыйКласс> базовыеКлассы = подклассы;
Применение wildcard-а позволяет демонстрировать ковариантность: список подклассы
интерпретируется как List
базового типа, что позволяет нам обрабатывать элементы базовыеКлассы
как объекты БазовыйКласс
. Обратите внимание: базовыеКлассы
доступны только для чтения, добавление объектов класса Подкласс
не допускается.
Использование Collections.unmodifiableList()
Если нужно получить List<БазовыйКласс>
, доступный только для чтения, можно использовать Collections.unmodifiableList()
:
List<БазовыйКласс> readOnlyBaseList = Collections.unmodifiableList(подклассы);
В этом случае список будет доступен исключительно для чтения, попытки добавления новых элементов вызовут исключение.
Использование List.copyOf()
для создания неизменяемого списка
С версии Java 9 можно использовать List.copyOf()
для создания неизменяемой копии списка:
List<БазовыйКласс> неизменяемыеБазовыеКлассы = List.copyOf(подклассы);
List.copyOf()
оптимизирован с учётом типовой безопасности и эффективности: если исходный список уже является неизменяемым и соответствует типу БазовыйКласс
, новый список не создаётся.
Ковариантность массивов и её плюсы и минусы
Рассмотрим ковариантность массивов в Java:
Подкласс[] массивПодклассов = new Подкласс[10];
БазовыйКласс[] массивБазовыхКлассов = массивПодклассов; // Всё корректно
Такое поведение отличается от работы с обобщенными коллекциями, где типы стираются и требуется аккуратность при приведении типов, чтобы избежать ошибок во время выполнения.
Двойное приведение типов — резервный путь
Иногда прибегают к двойному приведению типов: сначала к сырому типу, затем к целевому:
List rawList = подклассы;
List<БазовыйКласс> baseList = (List<БазовыйКласс>) rawList;
Важно использовать такой подход осторожно, чтобы не вызвать "загрязнение кучи".
Неизменяемость для безопасности — профилактика ошибок
Гарантия неизменяемости в списке предотвращает нежелательные модификации, а также устраняет необходимость в двойном приведении типов или любых других операциях, которые могут изменять список.
Визуализация
Представьте семейное дерево, где Подкласс
— это дети, а Базовый класс
— родители:
Семейное дерево
👴 (БазовыйКласс)
/ \
👦 👧
(Подкласс)
Преобразование List<Подкласс>
в List<БазовыйКласс>
:
До: Список 👦👧
После: Все представлены как 👴
Наследование — это аналог генетики в семье: дети наследуют характеристики от родителей.
Использование Map и stream для сложных преобразований
Для конвертации элементов из разных иерархий типов используйте stream()
в сочетании с map()
:
List<ДругойПодкласс> другиеПодклассы = new ArrayList<>();
List<БазовыйКласс> преобразованныйСписок = другиеПодклассы.stream()
.map(element -> (БазовыйКласс) element)
.collect(Collectors.toList());
Этот подход обеспечивает безопасное взаимодействие с разными типами.
Детализация преимуществ List.copyOf()
В документации Java SE 19 указывается, что List.copyOf()
полезен для создания неизменяемых коллекций и поддерживает типовую безопасность при использовании совместно с List.of()
.
Исключения времени выполнения — скрытые препятствия
Несоответствие типов в массивах может вызвать исключения времени выполнения, что сравнимо с тем, как если бы вы были выведены охранником из клуба. В обобщенных коллекциях же следует быть особенно внимательным из-за стирания типов для избежания ошибок во время выполнения.
Полезные материалы
- AngelikaLanger.com – Java Generics FAQs — подробные ответы на вопросы о Java Generics от Анжелики Лангер.
- [Java Generics and Collections [Книга]](https://www.oreilly.com/library/view/java-generics-and/0596527756/) — базовый учебник по Java Generics и Collections.
- Глава 13. Бинарная совместимость — обзор бинарной совместимости в Java.
- Урок: Generics (Updated) (Java™ Tutorials) — официальное руководство Oracle по Java Generics.
- Избегайте сырых типов – Java Practices — советы по работе с обобщениями.
- Глава 4. Типы, значения и переменные — особенности стирания типов в обобщениях, согласно спецификации Java.
- Понимание ковариантности и контравариантности в Java — разъяснение ковариантности и контравариантности в Java.