首页 > 代码库 > 编程错误实例的剖析[2]内部非静态类的反射

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

内部非静态类的反射


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


但今天还是栽了跟头。

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

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

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

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


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