Отражение Java: инициализируйте объект, используя интерфейс, но используя строковое значение для имени класса - PullRequest
0 голосов
/ 27 июня 2018

Допустим, у меня есть следующие настройки

и interface

public interface TestInterface {

  public void draw();
}

и две реализации

public class Square implements TestInterface {

  @Override
  public void draw() {
    System.out.println("Square");

  }

}

и

public class Circle implements TestInterface {

  @Override
  public void draw() {
    System.out.println("Circle");

  }

}

Теперь я легко могу сделать

TestInterface a = new Square();
a.draw();

и я правильно получаю Square. Затем я хотел попробовать отражение.

Class<?> clazz = null;
    try {
      clazz = Class.forName(className);
    } catch (ClassNotFoundException e) {
      // TODO Auto-generated catch block
      e.printStackTrace();
    }
    Constructor<?> constructor = null;
    try {
      constructor = clazz.getConstructor();
    } catch (NoSuchMethodException | SecurityException e1) {
      // TODO Auto-generated catch block
      e1.printStackTrace();
    }
    Object instance = null;
    try {
      instance = constructor.newInstance();
    } catch (InstantiationException | IllegalAccessException | IllegalArgumentException
        | InvocationTargetException e) {
      // TODO Auto-generated catch block
      e.printStackTrace();
    }

    Method method = null;
    try {
      method = instance.getClass().getMethod(methodName);
    } catch (NoSuchMethodException | SecurityException e) {
      // TODO Auto-generated catch block
      e.printStackTrace();
    }
    try {
      method.invoke(instance);
    } catch (IllegalAccessException | IllegalArgumentException | InvocationTargetException e) {
      // TODO Auto-generated catch block
      e.printStackTrace();
    }

и для className как some.package.Square и methodName как draw я снова получаю Square. Что правильно.

Проблема в том, что у моего приложения есть доступ к interface, но нет реальных реализаций. Следовательно, я знаю, какие методы вызывать, но я также должен указать реализованные классы и не знаю, в каком пакете они могут находиться. Что если я знаю только имя класса, но не пакет?

Есть ли способ, где я все еще могу использовать форму инициализации

TestInterface a = new <some_parameter>();
a.draw();

Есть ли способ обобщить это? Или подход, использующий рефлексию, который я показал выше, является единственным способом достижения чего-то подобного? И, наконец, будет ли какая-то разница, если я использую абстрактный класс вместо интерфейса?

Ответы [ 2 ]

0 голосов
/ 28 июня 2018

Вам необходимо пройти:

@param      className   the fully qualified name of the desired class.

Когда у вас есть, например, три класса с тем же именем, но в разных пакетах

--package1
----Test
--package2
----Test
Main
Test

А в Main у вас есть:

public static void main(String[] args) throws UnsupportedEncodingException {

        Class<?> clazz = null;
        try {
            clazz = Class.forName("Test");
        } catch (ClassNotFoundException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        }
}

Он будет называть тот, который является уровнем Main. Для вызова остальных вам нужно будет передать полностью недооцененное имя.

    clazz = Class.forName("package1.Test");
    clazz = Class.forName("package2.Test");

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

0 голосов
/ 27 июня 2018

Вы должны знать полное название класса. Только имя класса недостаточно, чтобы загрузить его в память и использовать для отражения. Однако вы можете определить реализации вашего интерфейса, используя библиотеку reflections .

Может быть, эта ветка обсуждения может вам помочь: Как я могу получить список всех реализаций интерфейса программно в Java?

...