【问题标题】:Java: properly checked class instantiation using reflectionJava:使用反射正确检查类实例化
【发布时间】:2011-04-12 23:59:36
【问题描述】:

我正在尝试使用一种最简单的反射形式来创建类的实例:

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 获得的未知 Class 对象进行类型转换会产生警告:

类型安全:从 Class 到 Class

我尝试过使用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&lt;? extends My&gt;. Use the form Class&lt;?&gt; instead since further generic type information will be erased at runtime. 所以我想我不能使用instanceof 方法。

我该如何摆脱它,我应该如何正确地做到这一点?是否可以在没有这些警告的情况下使用反射(即不忽略或抑制它们)?

【问题讨论】:

  • 不确定强类型在这里为您带来了什么。当您执行 Class.forName 时,编译时无法保证生成的类的类型。无论如何,您都需要转换为 (My)。打字为您带来了哪些额外的编译时安全性?
  • 我只是在玩好东西,并且急切地想知道 Sun 的建筑师在设计这个警告时抽了什么样的锅。看来终于有答案了。

标签: java reflection compiler-warnings type-safety


【解决方案1】:

这就是你的做法:

/**
 * 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");

【讨论】:

  • 一个小提示。您可以使用单次 throw ReflectiveOperationException 缩短签名。
  • @Leo - 同意,前提是 Java 7 或更高版本。对于 Java 5 和 6,您需要所有三种异常类型
【解决方案2】:

我认为你可以这样做:

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();

【讨论】:

  • 不,它不起作用。警告不会消失。尝试使用 javac 1.6.0_22(来自 OpenJDK)和 1.6.0_24(来自 Sun 的 JDK)。
  • 奇怪,它在 Eclipse 中编译时没有警告。哪条线路发出警告?我的代码中没有强制转换,所以不可能是同一个异常。
  • 您在 Eclipse 中没有收到警告,因为编译器已被指示禁止未经检查的强制转换警告。
  • @Grey:您可能无法避免警告,但@Justin 给您的是运行时检查加载的类是否可分配给My,它允许您正确而优雅地失败。到那时,只需取消警告 (@SuppressWarnings("unchecked")),没有办法绕过它,并且您已经证明它是无关紧要的,因为如果您使用该代码,您就不会有类型问题。
  • 我在使用来自 sun 的 jdk1.6.0_23 时没有收到任何警告。你得到了什么确切的警告?
【解决方案3】:

看到这个问题How do I address unchecked cast warnings?。我发现在反射方面,你最好负责任地使用@SuppressWarnings("unchecked")

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2012-12-16
    • 1970-01-01
    • 2013-01-18
    • 2013-07-03
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多