Troubleshooting
以下示例显示了在类上进行反思时可能会遇到的典型错误。
编译器警告:“注意:...使用未经检查或不安全的操作”
调用方法时,将检查并可能转换参数值的类型。 ClassWarning调用getMethod()会引起典型的未经检查的转化警告:
import java.lang.reflect.Method;
public class ClassWarning {
void m() {
try {
Class c = ClassWarning.class;
Method m = c.getMethod("m"); // warning
// production code should handle this exception more gracefully
} catch (NoSuchMethodException x) {
x.printStackTrace();
}
}
}
$ javac ClassWarning.java
Note: ClassWarning.java uses unchecked or unsafe operations.
Note: Recompile with -Xlint:unchecked for details.
$ javac -Xlint:unchecked ClassWarning.java
ClassWarning.java:6: warning: [unchecked] unchecked call to getMethod
(String,Class<?>...) as a member of the raw type Class
Method m = c.getMethod("m"); // warning
^
1 warning
许多库方法已经用通用声明进行了改进,包括Class中的几个。由于c
被声明为* raw *类型(没有类型参数),而getMethod()的对应参数是参数化类型,因此发生未经检查的转换。需要编译器生成警告。 (请参见Java 语言规范,Java SE 7 版,第Unchecked Conversion和方法调用转换节。)
有两种可能的解决方案。最好修改c
的声明以包含适当的泛型类型。在这种情况下,声明应为:
Class<?> c = warn.getClass();
或者,可以使用有问题的语句之前的 预定义 注解@SuppressWarnings明确禁止警告。
Class c = ClassWarning.class;
@SuppressWarnings("unchecked")
Method m = c.getMethod("m");
// warning gone
Tip:
作为一般原则,不应忽略警告,因为警告可能表明存在错误。应适当使用参数化声明。如果这是不可能的(可能是因为应用程序必须与库供应商的代码进行交互),请使用@SuppressWarnings注解 违规行。
构造函数不可访问时的 InstantiationException
如果try创建该类的新实例并且零参数构造函数不可见,则Class.newInstance()将引发InstantiationException。 ClassTrouble示例说明了生成的堆栈跟踪。
class Cls {
private Cls() {}
}
public class ClassTrouble {
public static void main(String... args) {
try {
Class<?> c = Class.forName("Cls");
c.newInstance(); // InstantiationException
// production code should handle these exceptions more gracefully
} catch (InstantiationException x) {
x.printStackTrace();
} catch (IllegalAccessException x) {
x.printStackTrace();
} catch (ClassNotFoundException x) {
x.printStackTrace();
}
}
}
$ java ClassTrouble
java.lang.IllegalAccessException: Class ClassTrouble can not access a member of
class Cls with modifiers "private"
at sun.reflect.Reflection.ensureMemberAccess(Reflection.java:65)
at java.lang.Class.newInstance0(Class.java:349)
at java.lang.Class.newInstance(Class.java:308)
at ClassTrouble.main(ClassTrouble.java:9)
Class.newInstance()的行为与new
关键字非常相似,并且会因new
失败的相同原因而失败。反映的典型解决方案是利用java.lang.reflect.AccessibleObject类,该类提供了抑制访问控制检查的能力。但是,此方法将不起作用,因为java.lang.Class不扩展AccessibleObject。唯一的解决方案是将代码修改为使用Constructor.newInstance(),它确实扩展了AccessibleObject。
Tip:
通常,出于Members类的创建新的类实例部分中所述的原因,最好使用Constructor.newInstance()。
在Members类的Constructor Troubleshooting部分中可以找到使用Constructor.newInstance()的潜在问题的其他示例。