文档

Java™ 教程-Java Tutorials 中文版
故障排除
Trail: The Reflection API
Lesson: Classes

故障排除

以下示例显示了在反映类时可能遇到的典型错误。

编译器警告:"Note: ... uses unchecked or unsafe operations"

当一个方法被调用时,参数值的类型被检查并且可能被转换。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() 的相应的参数是参数化类型,所以发生未经检查的转换。编译器需要生成警告。(参见 The Java Language Specification, Java SE 7 Edition 中的 Unchecked ConversionMethod Invocation Conversion 部分。)

有两种可能的解决方案。比较好的方案是修改 c 的声明以包含适当的泛型类型。在这种情况下,声明应该是:

Class<?> c = warn.getClass();

或者,可以在有问题的语句之前的使用预定义注解 @SuppressWarnings 明确抑制警告。

Class c = ClassWarning.class;
@SuppressWarnings("unchecked")
Method m = c.getMethod("m");  
// warning gone

提示: 作为一般原则,警告不应该被忽略,因为它们可能表明存在错误。应适当使用参数化声明。如果这不可能(可能是因为应用程序必须与库供应商的代码交互),请使用 @SuppressWarnings 注解违规的行。

构造函数不可访问时的 InstantiationException

Class.newInstance()如果尝试创建类的新实例,并且类的无参构造函数不可见时,将抛出 InstantiationExceptionClassTrouble 示例说明了生成的堆栈跟踪。


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


提示: 通常,出于 Members 课程中的 Creating New Class Instances 部分中描述的原因,创建新类实例最好使用 Constructor.newInstance()

使用 Constructor.newInstance() 的潜在问题的其他示例可以在 Members 课程的 Constructor Troubleshooting 部分中找到。


Previous page: Discovering Class Members
Next page: Members