Как сделать так, чтобы аргументы типа оператора Diamond были динамическими в Java?
У меня есть следующий интерфейс
public interface Splitter<T, V> {
V[] split(T arg);
}
Ниже приведена реализация метода фабрики, которую я использую, чтобы получить реализацию Splitter.
Реализация метода фабрики
public static <T, V> Splitter<T, V> getSplitter(Class<T> key1, Class<V> key2) {
if (key1 == Company.class && key2 == Department.class)
return (Splitter<T, V>) new CompanySplitterImpl();
// more cases
}
Ниже мой звонок на стороне клиента, который хорошо компилируется
Splitter<Company, Department> split = getSplitter(Company.class, Department.class);
Я хочу избежать тесной связи кода на стороне клиента с реализацией. Есть ли способ избежать жестко закодированных параметров типа, т.е. избежать использования компании и отдела (Splitter<Company, Department>
) на стороне вызываемого абонента и использовать вместо этого некоторую переменную? Есть ли выход, через который они могут быть загружены из какого-либо внешнего файла свойств?
К вашему сведению: я не уверен в его целесообразности на Java?
4 ответа
Одна вещь, которую вы могли бы сделать, - это чтобы ваша фабрика ничего не знала о конкретных реализациях, и вместо этого попросила себя зарегистрироваться в ней (или содержать предварительно определенный список) и спросить каждую реализацию, может ли она обрабатывать типы или нет. Например, для заданного списка, как в примере выше:
public class SplitterFactory {
private Set<Splitter> splitters = new HashSet<>() {{
add(new CompanySplitterImpl());
}};
public static <T, V> Splitter<T, V> getSplitter(Class<T> key1, Class<V> key2)
{
for (Splitter splitter : splitters) {
if (splitter.canAccept(key1, key2)) {
return splitter;
}
// no matched splitter
}
}
Очевидно, что это очень наивное решение, и вы могли бы реализовать поиск более эффективно. Если вы не знаете свои типы во время компиляции, у вас также может быть механизм регистрации с фабрикой для включения новых во время выполнения. Поскольку теперь сам Splitter отвечает за то, чтобы сообщать, какие типы он может обрабатывать, он полностью расширяем.
Вы можете создать простой класс карты, в котором вы можете перечислить их до:
public final class SplitterMap {
private final List<SplitterType<?, ?>> list = new ArrayList<>();
private class SplitterType<T, V> {
private final Class<T> key1;
private final Class<V> key2;
private final Class<? extends Splitter<T, V>> clazz;
private SplitterType(Class<?> key1, Class<?> key2, Class<? extends Splitter<T, V> clazz) {
this.key1 = key1;
this.key2 = key2;
this.clazz = clazz;
}
private boolean matches(Class<?> key1, Class<?> key2) {
return this.key1 == key1 && this.key2 == key2;
}
}
public <T, V> void put(Class<T> key1, Class<V> key2, Class<? extends Splitter<T, V> clazz) {
list.add(new SplitterType<T, V>(key1, key2, clazz));
}
public <T, V> Splitter<T, V> get(Class<T> key1, Class<V> key2) {
for (SplitterType<?, ?> type : list) {
if (type.matches(key1, key2)) {
try {
return ((SplitterType<T, V>) type).clazz.newInstance();
} catch (Exception e) {
}
}
}
return null; // not found
}
}
Тогда вы можете просто сделать:
SplitterMap map = new SplitterMap();
map.put(Company.class, Department.class, CompanySplitterImpl.class);
Splitter<Company, Department> splitter = map.get(Company.class, Department.class);
Не очень хороший способ, но одним из способов будет:
String companyClass = "Company";
String departmentClass = "Department";
Splitter split = getSplitter(Class.forName(companyClass), Class.forName(departmentClass));//raw splitter
System.out.println(split.split(new Company()));//you could use reflection here to create instance from companyClass String.
Во-первых, я предполагаю, что вы хотите что-то вроде
Splitter<Company, Department> s = Splitters.getSplitter()
Что невозможно без размышлений, из-за
- стирание типа
- Перегрузка возвращаемого типа пока недоступна в Java
Во-вторых, вы злоупотребляете шаблоном FactoryMethod. Который должен выглядеть больше так:
interface Splitter<T, V> {
V[] split(T arg);
}
interface SplitterFactory {
<T, V> Splitter<T, V> getSplitter();
}
class CompanySplitterFactory implements SplitterFactory {
@Override
public Splitter<Company, Department> getSplitter() {
return new CompanySplitterImpl();
}
}