Вопрос или проблема
По причинам обобщенности у меня есть некоторые компоненты, которые имеют следующий телесный вид:
@Component
public class MyClass<C> extends SomeAbstractClass<A, C> {
public MyClass(SomeClass1 someClass1, SomeClass2 someClass2) {
super(superMethod(A.class, C.class));
}
}
MyClass
– это просто обобщенный каркас моих классов, они могут или не могут иметь какие-либо объекты в конструкторе (представленные SomeClass1
и SomeClass2
). Абстрактный суперкласс принимает два класса, здесь A
указан явно, он не является обобщенным, поэтому A.class
является действительным. Но C.class
не является действительным, поскольку это обобщенный класс.
Поэтому я попробовал вот это:
@Component
public class MyClass<C> extends SomeAbstractClass<A, C> {
public MyClass(SomeClass1 someClass1, SomeClass2 someClass2, Class<C> clazz) {
super(superMethod(A.class, clazz));
}
}
Этот код компилируется, но Spring не может внедрить Class<C> clazz
, как я могу это сделать?
Редактирование:
Я также пробовал что-то, что работает, но мне это не очень нравится. Я создаю класс для каждого обобщенного типа, как таковой:
@Component
public class MyClassWithoutGeneric extends SomeAbstractClass<A, B> {
public MyClassWithoutGeneric(SomeClass1 someClass1, SomeClass2 someClass2) {
super(superMethod(A.class, B.class));
}
}
То, что мне не нравится в этом решении, это то, что оно не очень универсально и не поддерживаемо.
Спасибо
Я нашел решение, которое мне больше нравится:
Теперь у меня есть обертка класса MyClassFactory
, которая имеет необходимые зависимости и выглядит примерно так:
@Component
public class MyClassFactory {
private final SomeClass1 someClass1;
private final SomeClass2 someClass2;
public MyClassFactory(SomeClass1 someClass1, SomeClass2 someClass2) {
this.someClass1 = someClass1;
this.someClass2 = someClass2;
}
public <C> MyClass<C> create(Class<C> clazz) {
return new MyClass<>(someClass1, someClass2, clazz);
}
}
Ответ или решение
Вам необходимо создать обобщенные классы в Spring, при этом сталкиваясь с проблемой внедрения типов, связанных с параметрами типа. Как вы уже заметили, прямое внедрение Class<C>
в конструктор вашего класса MyClass<C>
не работает в контексте Spring, поскольку контейнер не знает, какой конкретный тип C необходимо использовать при создании экземпляра MyClass
.
Вы уже предложили решение с использованием фабричного класса MyClassFactory
, и это действительно разумный подход. Позвольте мне объяснить его подробнее и привести дополнительные рекомендации.
Решение через фабричный класс
Ваш фабричный класс MyClassFactory
позволяет инкапсулировать зависимость от SomeClass1
и SomeClass2
, а также предоставляет метод для создания экземпляров MyClass<C>
с заданным типом. Вот как это выглядит:
@Component
public class MyClassFactory {
private final SomeClass1 someClass1;
private final SomeClass2 someClass2;
public MyClassFactory(SomeClass1 someClass1, SomeClass2 someClass2) {
this.someClass1 = someClass1;
this.someClass2 = someClass2;
}
public <C> MyClass<C> create(Class<C> clazz) {
return new MyClass<>(someClass1, someClass2, clazz);
}
}
Также ваш MyClass
остаётся обобщённым классом:
@Component
public class MyClass<C> extends SomeAbstractClass<A, C> {
public MyClass(SomeClass1 someClass1, SomeClass2 someClass2, Class<C> clazz) {
super(superMethod(A.class, clazz));
}
}
Использование фабрики
Теперь, чтобы создать экземпляр MyClass
, вам нужно будет сделать это через MyClassFactory
. Пример использования может выглядеть так:
@Autowired
private MyClassFactory myClassFactory;
public void someMethod() {
MyClass<SomeConcreteClass> instance = myClassFactory.create(SomeConcreteClass.class);
// Используйте instance по мере необходимости
}
Другие рекомендации
-
Параметры типа в Spring: Иногда параметры типа могут быть переданы через
@Qualifier
в Spring, если вы используете разные реализации одного интерфейса. Однако это не решает вашу проблему с обобщёнными классами напрямую. -
Использование рефлексии: Если вам действительно необходимо сделать что-то более динамичное, можно рассмотреть возможность использования рефлексии для инициализации классов во время выполнения. Однако это увеличит сложность и может негативно повлиять на читаемость кода.
-
Создание специализированных фабрик: В зависимости от вашей логики, вы можете создавать специализированные фабрики для некоторых типичных реализаций
C
, что упростит создание объектов.
Заключение
Использование фабричного класса, который инкапсулирует создание экземпляров обобщенного класса, является хорошим и чистым решением для вашей задачи. Это делает ваш код более поддерживаемым и гибким, предотвращая создание множества специализированных классов для каждой комбинации параметров типа.