Использование отражения в Java для создания нового экземпляра с типом ссылочной переменной, установленной на имя нового класса экземпляра? - PullRequest
19 голосов
/ 07 февраля 2010

Все примеры, на которые я смотрю для размышления, показывают создание нового экземпляра неизвестной реализации и приведение этой реализации к его интерфейсу. Проблема в том, что теперь вы не можете вызывать какие-либо новые методы (только переопределения) в реализующем классе, так как ваша переменная ссылки на объект имеет тип интерфейса. Вот что у меня есть:

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», и я не знаю, каким может быть этот тип (он исходит из файла конфигурации), то как я могу использовать имя класса приведите его к реализацииType? Это вообще возможно?

Ответы [ 6 ]

29 голосов
/ 07 февраля 2010

Эта строка, кажется, подводит итог вашей проблемы:

Проблема в том, что теперь вы не можете вызывать какие-либо новые методы (только переопределения) для реализующего класса, так какПеременная ссылки на объект имеет тип интерфейса.

Вы довольно застряли в своей текущей реализации, поскольку вам нужно не только выполнить приведение, но и определить метод (ы), который выхочу позвонить на этот подкласс.Я вижу два варианта:

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. Другой вариант, который у вас есть, - расширить свое отражение от всего лишь Class / Object создания, включив в него Method отражение.Если вы можете создать Class из строки, переданной из файла конфигурации, вы также можете передать имя метода из этого файла конфигурации и, с помощью отражения, получить экземпляр самого Method из вашего объекта Class,Затем вы можете вызвать invoke (http://java.sun.com/javase/6/docs/api/java/lang/reflect/Method.html#invoke(java.lang.Object, java.lang.Object ...)) на Method, передав экземпляр вашего класса, который вы создали.Я думаю, что это поможет вам получить то, что вы ищете.

Вот код, который можно использовать в качестве примера.Обратите внимание, что я взял на себя смелость кодировать параметры для методов.Вы также можете указать их в конфиге, и вам нужно будет подумать об именах их классов, чтобы определить их Class объекты и экземпляры.

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
3 голосов
/ 05 декабря 2012
//====Single Class Reference used to retrieve object for fields and initial values. Performance enhancing only====          
Class<?>    reference           =   vector.get(0).getClass();
Object      obj                 =   reference.newInstance();
Field[]     objFields           =   obj.getClass().getFields(); 
2 голосов
/ 08 февраля 2010

В качестве дополнения к ответу akf вы можете использовать проверки instanceof вместо вызовов 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();
}

Обратите внимание на либеральное жесткое кодирование некоторых значений. В любом случае основные моменты:

  1. Используйте instanceof, а не equals (). Во всяком случае, он будет лучше взаимодействовать при рефакторинге.
  2. Обязательно перехватывайте эти ошибки времени выполнения и ошибки безопасности.
1 голос
/ 07 февраля 2010

Я не совсем уверен, что правильно понял ваш вопрос, но, кажется, вы хотите что-то вроде этого:

    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 можно определить на уровне метода или на уровне класса, т. Е. <T extends InterfaceType>

1 голос
/ 07 февраля 2010

Вы хотите иметь возможность передать класс и получить безопасный для типов экземпляр этого класса? Попробуйте следующее:

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();
}
0 голосов
/ 07 февраля 2010

Если бы вы знали Class из ImplementationType, вы могли бы создать его экземпляр. Поэтому то, что вы пытаетесь сделать, невозможно.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...