Как обратиться к функционалу класса по имени класса переданного в строке String?
И так есть класс, в котором есть вложенные статичные классы, в которых лежат enum классы. Обращение к enum классу выглядит так: MainClass.Inner1.Enum2.getValueList(). Мне на вход метода приходит в виде строки String имена классов: Inner1, Enum2(например), после чего мне надо составить запрос указанный выше, чтобы получить список значений enum класса. По идее здесь речь идёт о Reflection, но сообразить как это сделать не могу. Вопрос как это сделать? Если вариантов несколько какой лучше? Или хотя бы куда смотреть?
Ответы (2 шт):
Ну к примеру что-то такое без всякого Reflection:
public class EnumDemo {
private static final Map<String, EnumContainer<?>> lookup = Stream.of(Inner1.values(), Inner2.values())
.flatMap(Stream::of)
.collect(Collectors.toMap(EnumContainer::flatName, Function.identity()));
interface EnumContainer<T> {
List<T> valueList();
String flatName();
}
enum Inner1 implements EnumContainer<String> {
Enum1(List.of("A", "B", "C")),
Enum2(List.of("A", "B", "C", "D"));
private final List<String> valueList;
Inner1(List<String> valueList) {
this.valueList = valueList;
}
@Override
public List<String> valueList() {
return valueList;
}
@Override
public String flatName() {
return Inner1.class.getSimpleName() + "." + this.name();
}
}
enum Inner2 implements EnumContainer<Integer> {
Enum3(List.of(1, 2, 3)),
Enum4(List.of(1, 2, 3, 4));
public final List<Integer> valueList;
Inner2(List<Integer> valueList) {
this.valueList = valueList;
}
@Override
public List<Integer> valueList() {
return valueList;
}
@Override
public String flatName() {
return Inner2.class.getSimpleName() + "." + this.name();
}
}
private static String dumpEnum(String s) {
return lookup.get(s).valueList().stream().map(Object::toString).collect(Collectors.joining(","));
}
public static void main(String[] args) {
System.out.println(dumpEnum("Inner1.Enum1"));
System.out.println(dumpEnum("Inner1.Enum2"));
System.out.println(dumpEnum("Inner2.Enum3"));
System.out.println(dumpEnum("Inner2.Enum4"));
}
}
Конечно можно и с помощью Reflection. К примеру как-то так (заранее извиняюсь за стиль):
Optional<Class<?>> inner = Arrays.stream(EnumDemo.class.getDeclaredClasses())
.filter(Class::isEnum)
.filter(clazz -> clazz.getSimpleName().contains("Inner1"))
.findFirst();
EnumContainer<?> o = inner.map(clazz -> {
try {
return clazz.getMethod("valueOf", String.class).invoke(null, "Enum1");
} catch (IllegalAccessException | NoSuchMethodException | InvocationTargetException e) {
e.printStackTrace();
}
return null;
})
.filter(r -> EnumContainer.class.isAssignableFrom(r.getClass()))
.map(EnumContainer.class::cast)
.get();
В результате сделал нечто подобное. В реальности вложенность меньше за счёт использования отдельного метода для поиска класса. В целом смысл такой: парсим строку в которой указан путь - MainClass.Inner1.Enum2. Дальше получаем список классов MainClass.class.getDeclaredClasses() в нём ищем класс с именем из списка, затем в его списке классов ищем класс со следующим именем и т.д. пока не добираемся до enum класса. Откуда берем список enum значений cc.getEnumConstants().
for (int i = 0; i < MainClass.class.getDeclaredClasses().length; i++) {
if(MainClass.class.getDeclaredClasses()[i].toString().contains("Inner1")){
Class<?> c = (MainClass.class.getDeclaredClasses()[i];
for (int j = 0; j <c.class.getDeclaredClasses().length; j++) {
if(c.class.getDeclaredClasses()[j].toString().contains("Status")){
Class<?> cc = c.class.getDeclaredClasses()[j];
System.out.println(cc.getEnumConstants());
}
}
}
}