Ну, работа с типами, указанными во время выполнения, т. Е. Неизвестным во время компиляции, является определением Reflection. Вы не можете выполнять рефлексивные операции без Reflection.
Что вы можете сделать, это создать экземпляр вашего функционального интерфейса, который может создавать экземпляр класса без использования отражения, т. Е. Все, что было разрешено до вызова метода интерфейса, подобно ссылке на метод для члена, известного во время компиляции работает во время выполнения:
public static MyFactory getRuntimeClass(Class<? extends MyClass> cls) {
try {
MethodHandles.Lookup l = MethodHandles.lookup();
MethodHandle c = l.findConstructor(cls,
MethodType.methodType(void.class, String.class));
MethodType ft = c.type().changeReturnType(MyClass.class);
return (MyFactory)LambdaMetafactory.metafactory(l, "getInstance",
MethodType.methodType(MyFactory.class), ft, c, ft).getTarget().invokeExact();
}
catch(RuntimeException | Error t) {
throw t;
}
catch(Throwable t) {
throw new IllegalArgumentException(t);
}
}
конечно, генерируя экземпляр является отражающей операции со всеми его недостатками, как обнаружение ошибок только на время выполнения. Поскольку нет безопасности проверок времени компиляции, не было бы никакой универсальной безопасности, если вы попробуете это с помощью универсального функционального интерфейса.
Если вы изменили интерфейс и не забыли адаптировать этот код, может случиться так, что вы даже не заметите ошибку при выполнении этого метода getRuntimeClass
, но только тогда, когда вы на самом деле пытаетесь вызвать метод интерфейса для сгенерированного экземпляра ,
В отличие от реальных ссылок на методы, это не имеет никакого кэширования. Это нормально, если вы только сопоставляете имена классов с MyFactory
экземплярами во время инициализации и сохраняете экземпляры MyFactory
. Но если вам придется неоднократно сопоставлять имена классов с MyFactory
экземплярами, возможно, с теми же именами, вы можете захотеть их запомнить. Самое простое решение:
static ClassValue<MyFactory> MY_FACTORIES = new ClassValue<MyFactory>() {
protected MyFactory computeValue(Class<?> type) {
return getRuntimeClass(type.asSubclass(MyClass.class));
}
};
, который может быть использован как
MyClass mySubClass = MY_FACTORIES.get(Class.forName(args[0])).getInstance("test");
Я думаю, вы поняли, что ссылка метод. Просто используйте стандартное отражение, 'Class # newInstnace'. –
'cls :: new' пытается получить ссылку на конструктор класса. Он терпит неудачу, потому что 'Class' не имеет открытых конструкторов, принимающих один аргумент' String'. Невозможно делать то, что вы хотите, с помощью ссылок на методы. Я предлагаю просто пойти с тем, что предлагает @SotiriosDelimanolis. –
@Sotirios Delimanolis: 'Class.newInstance' поддерживает только конструктор по умолчанию. – Holger