Java 教程是为 JDK 8 编写的。本页中描述的示例和实践未利用在后续版本中引入的改进。
以下示例显示了在反映类时可能遇到的典型错误。
当一个方法被调用时,参数值的类型被检查并且可能被转换。
调用 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 Conversion 和 Method Invocation Conversion 部分。)
有两种可能的解决方案。比较好的方案是修改 c
的声明以包含适当的泛型类型。在这种情况下,声明应该是:
Class<?> c = warn.getClass();
或者,可以在有问题的语句之前的使用预定义注解 @SuppressWarnings
明确抑制警告。
Class c = ClassWarning.class; @SuppressWarnings("unchecked") Method m = c.getMethod("m"); // warning gone
@SuppressWarnings
注解违规的行。
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
。
使用 Constructor.newInstance()
的潜在问题的其他示例可以在 Members 课程的 Constructor Troubleshooting 部分中找到。