Все примеры, на которые я смотрю для отражательного шоу, создающего новый экземпляр неизвестной реализации и бросающего ту реализацию к, он - интерфейс. Проблема с этим - то, что теперь Вы не можете назвать новые методы (только переопределения) на классе с реализацией, поскольку Ваша переменная ссылки на объект имеет интерфейсный тип. Вот то, что я имею:
Class c = null;
try {
c = Class.forName("com.path.to.ImplementationType");
} catch (ClassNotFoundException e) {
e.printStackTrace();
}
InterfaceType interfaceType = null;
try {
interfaceType = (InterfaceType)c.newInstance();
} catch (InstantiationException e) {
e.printStackTrace();
} catch (IllegalAccessException e) {
e.printStackTrace();
}
Если у меня только есть ссылка на "com.path.to. ImplementationType", и я не знаю то, чем тот тип мог бы быть (он прибывает из файла конфигурации), тогда как я могу использовать имя класса для кастинга его в ImplementationType? Это даже возможно?
Эта строка, кажется, подводит итог сути вашей проблемы:
Проблема в том, что теперь вы не можете вызывать новые методы (только переопределения) на реализующем классе, так как переменная ссылка на объект имеет тип интерфейса.
Вы застряли в своей текущей реализации, так как вам нужно не только попробовать кастинг, вам также нужно определение метода(ов), который вы хотите вызвать на этом подклассе. Я вижу два варианта:
1. Как уже говорилось, вы не можете использовать строковое представление имени класса для приведения вашего отраженного экземпляра к известному типу. Однако, вы можете использовать тест String
equals()
, чтобы определить, является ли ваш класс тем типом, который вам нужен, а затем выполнить жестко закодированное приведение:
try {
String className = "com.path.to.ImplementationType";// really passed in from config
Class c = Class.forName(className);
InterfaceType interfaceType = (InterfaceType)c.newInstance();
if (className.equals("com.path.to.ImplementationType") {
((ImplementationType)interfaceType).doSomethingOnlyICanDo();
}
} catch (Exception e) {
e.printStackTrace();
}
Это выглядит довольно уродливо, и это портит хороший управляемый конфигуратором процесс, который у вас есть. Я не советую вам это делать, это всего лишь пример.
2. Другой вариант, который у вас есть, это расширение вашего отражения от простого Класса
/Объекта
создания до включения отражения Метода
. Если вы можете создать Класс
из строки, переданной из конфигурационного файла, вы также можете передать имя метода из этого конфигурационного файла и через отражение получить экземпляр самого Метода
из вашего Класса
объекта. Затем можно вызвать call
(http://java.sun.com/javase/6/docs/api/java/lang/reflect/Method.html#invoke(java.lang.Object, java.lang.Object...))) с помощью Method
, передав экземпляр созданного Вами класса. Думаю, это поможет вам получить то, что вам нужно.
Вот пример кода. Обратите внимание, что я взял на себя смелость жестко закодировать параметры методов. Вы можете указать их и в конфигурационном файле, а также поразмышлять над их именами, чтобы определить их Class
obejcts и instances.
public class Foo {
public void printAMessage() {
System.out.println(toString()+":a message");
}
public void printAnotherMessage(String theString) {
System.out.println(toString()+":another message:" + theString);
}
public static void main(String[] args) {
Class c = null;
try {
c = Class.forName("Foo");
Method method1 = c.getDeclaredMethod("printAMessage", new Class[]{});
Method method2 = c.getDeclaredMethod("printAnotherMessage", new Class[]{String.class});
Object o = c.newInstance();
System.out.println("this is my instance:" + o.toString());
method1.invoke(o);
method2.invoke(o, "this is my message, from a config file, of course");
} catch (ClassNotFoundException e) {
e.printStackTrace();
} catch (NoSuchMethodException nsme){
nsme.printStackTrace();
} catch (IllegalAccessException iae) {
iae.printStackTrace();
} catch (InstantiationException ie) {
ie.printStackTrace();
} catch (InvocationTargetException ite) {
ite.printStackTrace();
}
}
}
и мой вывод:
this is my instance:Foo@e0cf70
Foo@e0cf70:a message
Foo@e0cf70:another message:this is my message, from a config file, of course
Вы хотите иметь возможность передать класс и получить типобезопасный экземпляр этого класса? Попробуйте следующее:
public static void main(String [] args) throws Exception {
String s = instanceOf(String.class);
}
public static <T> T instanceOf (Class<T> clazz) throws Exception {
return clazz.newInstance();
}
Если бы вы знали Класс
из Тип реализации
, вы могли бы создать его экземпляр. Итак, то, что вы пытаетесь сделать, невозможно.
В качестве дополнения к ответу akf можно использовать проверки экземпляров вместо вызовов String equals():
String cname="com.some.vendor.Impl";
try {
Class c=this.getClass().getClassLoader().loadClass(cname);
Object o= c.newInstance();
if(o instanceof Spam) {
Spam spam=(Spam) o;
process(spam);
}
else if(o instanceof Ham) {
Ham ham = (Ham) o;
process(ham);
}
/* etcetera */
}
catch(SecurityException se) {
System.err.printf("Someone trying to game the system?%nOr a rename is in order because this JVM doesn't feel comfortable with: “%s”", cname);
se.printStackTrace();
}
catch(LinkageError le) {
System.err.printf("Seems like a bad class to this JVM: “%s”.", cname);
le.printStackTrace();
}
catch(RuntimeException re) {
// runtime exceptions I might have forgotten. Classloaders are wont to produce those.
re.printStackTrace();
}
catch(Exception e) {
e.printStackTrace();
}
Обратите внимание на либеральное жесткое кодирование некоторых значений. В любом случае, основные моменты:
Я не совсем уверен, что правильно понял ваш вопрос, но, похоже, вам нужно что-то вроде этого:
Class c = null;
try {
c = Class.forName("com.path.to.ImplementationType");
} catch (ClassNotFoundException e) {
e.printStackTrace();
}
T interfaceType = null;
try {
interfaceType = (T) c.newInstance();
} catch (InstantiationException e) {
e.printStackTrace();
} catch (IllegalAccessException e) {
e.printStackTrace();
}
Где T может быть определено на уровне метода или на уровне класса, то есть