编程错误实例的剖析[2]内部非静态类的反射

来源:互联网 发布:网络重生txt全集下载 编辑:程序博客网 时间:2024/05/15 09:14

内部非静态类的反射


尽管在之前的学习中,对各种内部类的全限定名有着完善的总结:这里


但今天还是栽了跟头。

本次的案例是这样的,在试图对一个空参构造的内部类进行反射时,出现了错误。

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


由于内部类享有和类相同的地位,只不过在不使用反射API时其作用域被相应关键字限制,因此无论是反射成员内部类还是匿名内部类都是没问题的,我怀疑问题出在构造器参数上,我大概打印了一下,果不其然。

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 definedpublic Main$Inner(Main a){}}
恩,编译器报错了,显然其检测到了我们的内部类。

也许是我eclipse的问题,如果构造器参数不使用Main类型,那么就绕过了这个错误,在测试时,实际反射到的内部类取决于最后修改的类,即发生了名称冲突进行了class文件覆盖,而此时eclipse没有给出任何提示。

其实也不能将责任推给编译器,在我试图创建带"$"的类时,编译器提示不建议在自建类中使用该符,虽然$可以作为类名,我们在开发中也要慎用。


0 0
原创粉丝点击