内部非静态类的反射
尽管在之前的学习中,对各种内部类的全限定名有着完善的总结:这里
但今天还是栽了跟头。
本次的案例是这样的,在试图对一个空参构造的内部类进行反射时,出现了错误。
package com.thrblock.moretest; public class Main { class Inner{ public Inner(){} } public static void main(String[] args) throws ClassNotFoundException, InstantiationException, IllegalAccessException { Class<?> c = Class.forName("com.thrblock.moretest.Main$Inner"); Object inner = c.newInstance(); System.out.println(inner.getClass()); } }
运行结果:
Exception in thread "main" java.lang.InstantiationException: com.thrblock.moretest.Main$Inner
package com.thrblock.moretest; public class Main { class Inner{ public Inner(){} } public static void main(String[] args) throws ClassNotFoundException, InstantiationException, IllegalAccessException { Class<?> c = Class.forName("com.thrblock.moretest.Main$Inner"); System.out.println(c.getConstructors()[0].getParameterTypes().length); } }运行结果:
1
Inner类明明被声明成空参构造,那这个多出来的参数是什么呢?笔者回想了一些非静态成员内部类的实例化过程,应该是包装类.new 内部类(构造参数…);也就是说,除了我们定义的构造参数外,还需要一个对应包装类的实例,因此猜测多出来的构造参数对应于这个实例,验证一下,果然如此:
package com.thrblock.moretest; public class Main { class Inner{ public Inner(){} } public static void main(String[] args) throws ClassNotFoundException, InstantiationException, IllegalAccessException { Class<?> c = Class.forName("com.thrblock.moretest.Main$Inner"); System.out.println(c.getConstructors()[0].getParameterTypes()[0]); } }运行结果:
class com.thrblock.moretest.Main
那么,非静态内部类的反射应该追加一个对应外部类的实例:
package com.thrblock.moretest; import java.lang.reflect.Constructor; import java.lang.reflect.InvocationTargetException; public class Main { class Inner{ public Inner(){} } public static void main(String[] args){ Class<?> c; try { c = Class.forName("com.thrblock.moretest.Main$Inner"); Constructor<?> con = c.getConstructor(Main.class); Inner in = (Inner)con.newInstance(new Main()); System.out.println(in.getClass()); } catch (ClassNotFoundException | NoSuchMethodException | SecurityException | InstantiationException | IllegalAccessException | IllegalArgumentException | InvocationTargetException e) { System.out.println("Error happend"); } } }运行结果:
class com.thrblock.moretest.Main$Inner
这里已经出,反射成功的拿到对应内部类的实例了。
但问题还没有结束,既然使用$作为类名是合法的,那么假如我自己起一个和内部类相同名字的类会怎样呢:
package com.thrblock.moretest; class Main$Inner {//ERROR:The type Main$Inner is already defined public Main$Inner(Main a){} }恩,编译器报错了,显然其检测到了我们的内部类。
也许是我eclipse的问题,如果构造器参数不使用Main类型,那么就绕过了这个错误,在测试时,实际反射到的内部类取决于最后修改的类,即发生了名称冲突进行了class文件覆盖,而此时eclipse没有给出任何提示。
其实也不能将责任推给编译器,在我试图创建带"$"的类时,编译器提示不建议在自建类中使用该符,虽然$可以作为类名,我们在开发中也要慎用。
原文地址:http://blog.csdn.net/shuzhe66/article/details/40977843