2017-02-05 3 views
-1

У меня есть имена классов, неизвестные моему заводскому классу, которые я получил через вход во время выполнения.Получить ссылку метода из объекта класса в JAVA 8

В настоящее время у меня есть решение использовать отражение для создания экземпляров этих классов в моем классе фабрики, но я хочу посмотреть, можно ли передать метод методу new этих классов на мой заводский класс, чтобы я не делал нужно использовать отражение. например .:

public interface MyFactory { 
    MyClass getInstance(String s); 
} 
public static MyFactory getRuntimeClass(Class<? extends MyClass> cls) { 
    return cls::new; 
} 
public static void main(String[] args) { 
    MyClass mySubClass = getRuntimeClass(Class.forName(args[0]).asSubClass(MyClass.class)).getInstance("test"); 
} 

выше не работает, потому что эталонный метод new нельзя получить из объекта класса cls.

Как вы исправить вышеуказанное с помощью ссылки на метод, не возвращаясь назад к использованию отражения?

P.S. Да, у меня есть код для проверки ввода, все подклассы имеют конструктор, который принимает строку в качестве входного.

+0

Я думаю, вы поняли, что ссылка метод. Просто используйте стандартное отражение, 'Class # newInstnace'. –

+0

'cls :: new' пытается получить ссылку на конструктор класса. Он терпит неудачу, потому что 'Class' не имеет открытых конструкторов, принимающих один аргумент' String'. Невозможно делать то, что вы хотите, с помощью ссылок на методы. Я предлагаю просто пойти с тем, что предлагает @SotiriosDelimanolis. –

+0

@Sotirios Delimanolis: 'Class.newInstance' поддерживает только конструктор по умолчанию. – Holger

ответ

1

Ну, работа с типами, указанными во время выполнения, т. Е. Неизвестным во время компиляции, является определением 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"); 
Смежные вопросы