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()将引发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

Tip:

通常,出于Members类的创建新的类实例部分中所述的原因,最好使用Constructor.newInstance()

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