Java反射创建对象-自适应构造器

Java的反射非常强大,它能访问对象的私有field、method,私有构造器,甚至私有的内部类和匿名内部类。反射就像是Java提供的后门,对OOP来说它提供了一种非安全的访问方式。它是一种特殊手段,一般情况下我们不使用,因为反射可能非安全地修改对象数据,需要写更多的捕获异常代码,代码可读性差些。另外能通过反射来操作类对象,该类的.class字节码文件必须首先被虚拟机加载。

下面这个例子,来源于《Java编程思想》P667“Generics”一章的习题22,题目大意就是通过已知的类标签(Class<?>)、反射来构造该类的一个实例,可以调用该类的带参数构造函数。这个例子中除了反射,还用到了jdk1.5开始提供的泛型和可变参数。

我们先来了解下泛型,Java中的泛型,最初是为在容器对象中的元素提供编译时的类型安全检查(bug越早发现越好),实际通过利用泛型能写出更通用的方法,减少重复代码;但是Java为了向之前版本的代码兼容,所以在运行时擦除了泛型信息,因此它的泛型没有C++的彻底。由于运行时不保留类型信息,所以我们在这个例子中加入一个Class kind成员属性来保存泛型的实际类型信息。可变参数一般在反射时用到,就是方法在该参数位置上能接受0个,一个,或任意多个参数;实际上Java会对传入的可变参数,生成一个数组,当一个参数都没有时,这个数组就是null,因此过程会多出一个创建数组的开销,这也是一般代码中不用的原因。

在这个做完这个习题后,发现除了通过反射调用带参的构造函数来创建对象,还能自动选择构造器,做了进一步的修改。通过调用create方法,传递构造器所需要的参数,就能找到对应的构造器执行实例化。当构造失败或者找不到对应的构造器时,就会返回null(可用“NULL对象模式”)。再改改差不多可以当做一个工具类来用了。

注意:House类的构造函数参数都是包装类,调用ojb = ctt.create(30);会对基本型int进行包装为Integer,所以能找到构造函数public House (Integer age);如果House类的构造函数参数是基本类型int,调用kind.getDeclaredConstructor(parameterTypes)方法会找不到构造函数抛出NoSuchMethodException,这种情况可以参照apache的一个工具类的org.apache.commons.lang.ClassUtils.wrapperToPrimitive(Class cls)方法,再次进行尝试。

/**
 * @author South Park
 */
import java.lang.reflect.Constructor;
import java.lang.reflect.InvocationTargetException;

class Building {}
class House extends Building {
	private Integer age = 20;
	private String name = "default name";
	private String address = "default address";
	public House() {}
	public House (Integer age) {
		this.age = age;
	}
	public House (Integer age, String name) {
		this.age = age;
		this.name = name;
	}
	public House (Integer age, String name, String address) {
		this.age = age;
		this.name = name;
		this.address = address;
	}
	public House (String name, Integer age) {
		this.age = age;
		this.name = name + "(2)";
	}
	private House (String name, String address) {//私有构造器
		this.address = address;
		this.name = name;
	}
	public String toString() {
		return "age:"+age+", name:"+name+", address:"+address;
	}
}

public class ClassTypeCapture<t> {
	Class<t> kind;
	public ClassTypeCapture(Class<t> kind) {
		this.kind = kind;
	}
	public <w extends="" object=""> T create(W... w) {
		try {
			if (w != null && w.length > 0) {
				Class<?>[] parameterTypes = new Class<?>[w.length];
				int index =0;
				for (W arg : w) {
					parameterTypes[index++] = arg.getClass();
				}
				Constructor<t> constructor = kind.getDeclaredConstructor(parameterTypes);
				constructor.setAccessible(true);//让私有构造函数也可以调用
				return constructor.newInstance(w);
			} else {
				return kind.newInstance();
			}
		} catch (InstantiationException e) {
			e.printStackTrace();
		} catch (IllegalAccessException e) {
			e.printStackTrace();
		} catch (NoSuchMethodException e) {
			e.printStackTrace();
		} catch (InvocationTargetException e) {
			e.printStackTrace();
		}
		return null;
	}
	public boolean isInstance(Object arg) {
		return kind.isInstance(arg);
	}	
	public static void main(String[] args) {
		ClassTypeCapture<house> ctt = new ClassTypeCapture<house>(House.class);
		House ojb = ctt.create();
		System.out.println(ojb);
		ojb = ctt.create(30);
		System.out.println(ojb);
		ojb = ctt.create(new Integer(31), "Star City");
		System.out.println(ojb);
		ojb = ctt.create(new Integer(32), "Star City", "JiuXianQiao");
		System.out.println(ojb);
		System.out.println("===================================================");
		ojb = ctt.create("South Park", 41);
		System.out.println(ojb);
		ojb = ctt.create(41, "South Park");
		System.out.println(ojb);
		ojb = ctt.create("my name", "my street");
		System.out.println(ojb);
	}
} /* Output:
age:20, name:default name, address:default address
age:30, name:default name, address:default address
age:31, name:Star City, address:default address
age:32, name:Star City, address:JiuXianQiao
===================================================
age:41, name:South Park(2), address:default address
age:41, name:South Park, address:default address
age:20, name:my name, address:my street
*/
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值