Java: правильно проверенное создание экземпляра класса с использованием отражения

Я пытаюсь использовать одну из самых простых форм отражения для создания экземпляра класса:

package some.common.prefix;

public interface My {
    void configure(...);
    void process(...);
}

public class MyExample implements My {
    ... // proper implementation
}

String myClassName = "MyExample"; // read from an external file in reality

Class<? extends My> myClass =
    (Class<? extends My>) Class.forName("some.common.prefix." + myClassName);
My my = myClass.newInstance();

Приведение типа к неизвестному объекту класса, который мы получили от Class.forName, приводит к предупреждению:

Type safety: Unchecked cast from Class<capture#1-of ?> to Class<? extends My>

Я пробовал использовать подход instanceof проверки:

Class<?> loadedClass = Class.forName("some.common.prefix." + myClassName);
if (myClass instanceof Class<? extends RST>) {
    Class<? extends My> myClass = (Class<? extends My>) loadedClass;
    My my = myClass.newInstance();
} else {
    throw ... // some awful exception
}

но это приводит к ошибке компиляции: Cannot perform instanceof check against parameterized type Class<? extends My>. Use the form Class<?> instead since further generic type information will be erased at runtime. Так что я не могу использовать подход instanceof.

Как мне избавиться от этого и как я должен сделать это правильно? Можно ли использовать рефлексию вообще без этих предупреждений (т.е. без их игнорирования или подавления)?


person GreyCat    schedule 12.04.2011    source источник
comment
Не уверен, что сильная типизация приносит вам здесь. Когда вы выполняете Class.forName, нет никакой гарантии времени компиляции типа класса, который будет получен. В любом случае вам нужно выполнить приведение к (Моему). Какую дополнительную безопасность во время компиляции дает вам набор текста?   -  person sudocode    schedule 12.04.2011
comment
Я просто играю в паиньку и с нетерпением хочу узнать, какую травку курили архитекторы Sun, разрабатывая это предупреждение. Похоже, все-таки есть ответ.   -  person GreyCat    schedule 14.04.2011


Ответы (3)


Вот как вы это делаете:

/**
 * Create a new instance of the given class.
 * 
 * @param <T>
 *            target type
 * @param type
 *            the target type
 * @param className
 *            the class to create an instance of
 * @return the new instance
 * @throws ClassNotFoundException
 * @throws IllegalAccessException
 * @throws InstantiationException
 */
public static <T> T newInstance(Class<? extends T> type, String className) throws
        ClassNotFoundException,
        InstantiationException,
        IllegalAccessException {
    Class<?> clazz = Class.forName(className);
    Class<? extends T> targetClass = clazz.asSubclass(type);
    T result = targetClass.newInstance();
    return result;
}


My my = newInstance(My.class, "some.common.prefix.MyClass");
person Simon G.    schedule 12.04.2011
comment
Небольшой намек. Вы можете сократить подпись, используя однократное исключение ReflectiveOperationException. - person Leo; 28.09.2016
comment
@Leo - Согласен, если он работает на Java 7 или выше. Для Java 5 и 6 вам нужны все три типа исключений. - person Simon G.; 29.09.2016

Я думаю, вы можете сделать это следующим образом:

Class<? extends My> myClass= null;
Class<?> loadedClass = Class.forName("some.common.prefix." + myClassName);
if(My.class.isAssignableFrom(loadedClass))
{
    myClass = loadedClass.asSubclass(My.class);
}
My my = myClass.newInstance();
person Justin Waugh    schedule 12.04.2011
comment
Нет, это не работает. Предупреждение не исчезнет. Пробовал с javac 1.6.0_22 (от OpenJDK) и 1.6.0_24 (от Sun JDK). - person GreyCat; 12.04.2011
comment
Странно, он компилируется без предупреждений в eclipse. Какая строка выдает предупреждение? В моем коде нет приведения, поэтому это не может быть такое же исключение. - person Justin Waugh; 12.04.2011
comment
Вы не получаете предупреждение в eclipse, потому что компилятору было дано указание подавить предупреждение о непроверенном приведении. - person tofarr; 12.04.2011
comment
@Grey: Возможно, вы не сможете избежать предупреждения, но @Justin дал вам проверку во время выполнения, что загруженный класс может быть назначен My, что позволяет вам правильно и изящно ошибиться. В этот момент просто отключите предупреждение (@SuppressWarnings("unchecked")), его невозможно обойти, и вы доказали, что оно не имеет значения, поскольку, если вы доберетесь до этого кода, у вас не будет проблемы с типом. - person Mark Peters; 12.04.2011
comment
Я не получаю никаких предупреждений, используя jdk1.6.0_23 от солнца. Какое именно предупреждение вы получаете? - person Justin Waugh; 12.04.2011
comment
Ваш текущий ответ работает нормально, используя asSubclass() для фактического приведения. Я принял ответ Саймона, потому что его ответ появился немного раньше. Голосую за ваш ответ, так как он тоже хорош :) Спасибо! - person GreyCat; 14.04.2011

См. этот вопрос Как реагировать на предупреждения о непроверенном приведении?. Я обнаружил, что когда речь идет об отражении, вам лучше просто ответственно использовать @SuppressWarnings("unchecked")

person tofarr    schedule 12.04.2011