В общем, выкладываю два варианта решения: одно, которое мы обсуждали с
Dmitry Roo и второе, подсмотренное на SOF. Оба рабочие. Мне больше нравится второе (с Supplier<>), оно компактнее и не надо городить свою фабрику.
import lombok.Getter;
import java.util.ArrayList;
import java.util.List;
import java.util.function.Supplier;
public class Test {
public static void main(String[] args) {
HandleB handleB = new HandleB();
handleB.print();
HandleC handleC = new HandleC();
handleC.print();
}
}
// ТИПЫ
@Getter
abstract class A {
private String a = "aaa";
}
@Getter
class B extends A {
private String b = "bbb";
}
@Getter
class C extends A {
private String c = "ccc";
}
class MyFactory{
private final String type;
public MyFactory(String type) {
this.type = type;
}
public A getObject(){
return switch (type){
case "B" -> new B();
case "C" -> new C();
default -> throw new IllegalStateException("Unexpected value: " + type);
};
}
}
// КЛАССЫ для работы с типами
class HandleB {
Commons<B> commons = new Commons<>("B");
B instanceB = commons.getObject();
List<B> list = commons.getList();
void print(){
System.out.println(instanceB.getB());
}
}
class HandleC {
Commons2<C> commons = new Commons2<>(C::new);
C instanceC = commons.getObject();
List<C> list = commons.getList();
void print(){
System.out.println(instanceC.getC());
}
}
// КЛАСС с шаблонным кодом
// вариант с фабрикой
class Commons<T extends A> {
MyFactory factory;
public Commons(String type) {
this.factory = new MyFactory(type);
}
List<T> getList() {
List<T> list = new ArrayList<>();
for (int i = 0; i < 3; i++) {
T item = getObject();
list.add(item);
}
return list;
}
T getObject() {
return (T) factory.getObject();
}
}
// вариант с Supplier<>
// https://stackoverflow.com/questions/299998/instantiating-object-of-type-parameter
class Commons2<T extends A> {
private final Supplier<T> supplier;
Commons2(Supplier<T> supplier) {
this.supplier = supplier;
}
List<T> getList() {
List<T> list = new ArrayList<>();
for (int i = 0; i < 3; i++) {
T item = getObject();
list.add(item);
}
return list;
}
T getObject() {
return supplier.get();
}
}
Осталось понять как это знание прикрутить к приложению на Spring Boot.
Как бы, логично Commons оформить аннотацией Service, но компоненты в Spring по умолчанию являются Singletone, а мне надо чтобы для каждого случая использования Commons "подключался" свой Supplier.
1. Можно сделать Commons не Singletone, a Prototype и в каждом экземпляре инициализировать свой Supplier.
2. Можно оставить Singletone, а в каждый метод где надо создавать объекты передавать нужный Supplier (выглядит несколько громоздко).
3. Можно не вводить Commons в контекст Spring (не помечать его аннотацией, а оставить обычным классом) и использовать обычным Java-способом - экземляр, инициализация, использование. Тут штука в том что помимо Supplier в Commons будут и другие зависимости, которые в случае со Spring внедряются самим спрингом и их достаточно просто указать. Без спринга их надо будет внедрять руками. Как бы не проблема, но зачем нам лишняя писанина? :)
В общем, пока думаю как сделать лучше. Если есть типовой способ - подскажите :)