Используя отражение в Java для создания нового экземпляра со ссылочным набором типа переменной к новому имени класса экземпляра?

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

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? Это даже возможно?

19
задан 7 February 2010 в 03:49
поделиться

5 ответов

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

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

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

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
29
ответ дан 30 November 2019 в 03:38
поделиться

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

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();
}
1
ответ дан 30 November 2019 в 03:38
поделиться

Если бы вы знали Класс из Тип реализации , вы могли бы создать его экземпляр. Итак, то, что вы пытаетесь сделать, невозможно.

0
ответ дан 30 November 2019 в 03:38
поделиться

В качестве дополнения к ответу 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();
}

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

  1. Использовать instanceof, а не equals(). Если что, то при рефакторинге это будет работать лучше.
  2. Не забудьте отловить и эти ошибки выполнения, и ошибки безопасности.
2
ответ дан 30 November 2019 в 03:38
поделиться

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

    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 может быть определено на уровне метода или на уровне класса, то есть

1
ответ дан 30 November 2019 в 03:38
поделиться
Другие вопросы по тегам:

Похожие вопросы: