Создание экземпляра обобщенного класса в Java: обход ограничений
Быстрый ответ
Чтобы создать экземпляр обобщённого класса в Java, нужно указать конкретные типы в угловых скобках:
ClassName<ConcreteType> obj = new ClassName<>();
Если предусмотрено несколько типов:
ClassName<Type1, Type2> obj = new ClassName<>();
Чтобы компилятор сам определил типы, используйте алмазный оператор <>
. Для примитивных типов применяются обёрточные классы, например:
MyClass<Integer> myObj = new MyClass<>();
Встречаются и более сложные ситуации, такие как стирание типов или создание экземпляра типа T
внутри класса. Давайте рассмотрим, как эти проблемы можно решить.
За рамками базовых знаний: продвинутое создание экземпляров
1. Ссылки на класс: решение проблемы стирания типов
Воспользуйтесь ссылкой на Class<T>
при создании конструктора обобщённого класса. Это поможет создавать экземпляры динамически, что подводит нас к решению проблемы стирания типов.
public class Foo<T> {
private Class<T> type;
public Foo(Class<T> type) {
this.type = type;
}
public T createInstance() throws IllegalAccessException, InstantiationException {
return type.newInstance();
}
}
2. Фабрики: создание объектов по шаблону
Чтобы создавать экземпляры T
используйте фабрику объектов. Это добавляет гибкости и сокращает количество исключений, связанных с использованием отражения.
public interface Factory<T> {
T create();
}
public class Foo<T> {
private Factory<T> factory;
public Foo(Factory<T> factory) {
this.factory = factory;
}
public T createInstance() {
return factory.create();
}
}
3. Поставщики Java 8: надёжные создатели экземпляров
В Java 8 представлен функциональный интерфейс Supplier<T>
, который прекрасно подходит для создания экземпляров обобщённых типов.
import java.util.function.Supplier;
public class Foo<T> {
private Supplier<T> supplier;
public Foo(Supplier<T> supplier) {
this.supplier = supplier;
}
public T createInstance() {
return supplier.get();
}
}
Визуализация
Это иллюстрация пути от абстрактного обобщённого типа к его реализации:
Generic Class<Invisible> myThing = new GenericClass<>();
👔 -> [SizeSmall, SizeMedium, SizeLarge, SizeXL]
GenericClass<SizeMedium> myPerfectShirt = new GenericClass<SizeMedium>();
Вот ваш экземпляр, идеально подходящий по типу:
Ваш выбор: [SizeMedium]
4. Риски работы с рефлексией: будьте внимательны
Помните, что работа с рефлексией может быть рискованной. Метод newInstance()
может вызвать проблемы, если у T
нет конструктора по умолчанию.
public T createInstance()
throws IllegalAccessException, InstantiationException {
return type.newInstance();
}
Type mySuperclass = getClass().getGenericSuperclass();
5. Фабрики вне рамок конструкторов без параметров: расширяем возможности
Если вам нужен объект T
без конструктора по умолчанию, фабрики предлагают удобное решение этого ограничения.
MyClass<String> myObj = new MyClass<>(String::new);
6. Стирание типов – спуск в "кроличью нору" Java
В рантайме обобщённые типы исчезают из-за стирания типов. Однако, наши стратегии помогут нам преодолеть этот нюанс с помощью вывода типов.
Type genericType = ((ParameterizedType)myObj.getClass().getGenericSuperclass()).getActualTypeArguments()[0];
Полезные материалы
- Урок: Обобщения (обновлено) (The Java™ Tutorials > Learning the Java Language) – Официальная документация Oracle по обобщениям в Java.
- Java generics type erasure: when and what happens? – Stack Overflow – Пояснения о стирании типов в Java на Stack Overflow.
- AngelikaLanger.com – Java Generics FAQs – Часто задаваемые вопросы и ответы по обобщениям в Java.
- Effective Java, 3rd Edition – Книга Джошуа Блоха о лучших практиках применения Java, включая обобщённые типы.
- Java – Generics – Учебник по обобщениям в Java с практическими примерами.
- Generics in Java – GeeksforGeeks – Подробное руководство по обобщённым типам с примерами использования.