研究泛型与反射之间的关系非常有趣。
我们知道,反射和泛型都是Java的一种动态技术。而不像继承和多态,是面向对象的技术。可以这么说,反射和泛型都像是为了弥补像继承和多态这些面向对象技术的不足而产生的。模式多是建立在面向对象技术基础上的,因而每种模式多多少少在动态性,或者说扩展性方面有些不足,我们就又结合了反射和泛型对模式进行一定的扩展,使它在动态性方面更符合我们的要求。
在将这些技术结合起来的过程中,我们多多少少会想到将泛型和反射结合起来。这是非常有趣的话题:范型和反射都使得Java有了一定的扩展性,但同时都有它自己的不足,而将这两种技术结合起来,是不是又能解决各自的不足,使得它们在动态性上更上一层楼呢?
正像前面所说的,泛型和反射可以相互促进,我们先来看看泛型是怎么帮助反射的。
我们知道,使用反射的一个最大的烦恼就是应用反射以后得到的基本上都是Object类型的对象,这种对象要使用,需要我们进行强制类型转化。而我们更知道,泛型的功能之一就是消除我们使用强制类型转化。
1.运行期内初始化对象
运行期内初始化对象是我们最常用的反射功能,但是我们通过反射在运行期内得到的对象的类型通常是Object类型的,而这个对象又需要我们在使用的时候进行强制类型转化。现在,有了反射,可以使我们不需要做强制类型转化这个工作。
假设我们已经有了两个类:
view plaincopy to clipboardprint?
public class Cls1 {
public void do1() {
// TODO Auto-generated method stub
System.out.println("cls1...");
}
}
public class Cls2 {
public void do2() {
// TODO Auto-generated method stub
System.out.println("cls2...");
}
}
我们需要在运行期内初始化这两个对象,我们可以设计如下的初始化方法:
public class Factory{
public static <U extends Object>U getInstance(String clsName)
{
try
{
Class<?> cls = Class.forName(clsName);
return (U) cls.newInstance();
}
catch(Exception e)
{
e.printStackTrace();
return null;
}
}
}
在这个方法里,我们其实是利用泛型在初始化方法里提前做了强制类型转化的工作,这样使得我们不必在使用的时候进行强制类型转化的工作。
它们的测试代码如下:
view plaincopy to clipboardprint?
Cls1 i1 = Factory.getInstance("fanxing.factory.dynaFactory.Cls1");
i1.do1();
Cls2 i2 = Factory.getInstance("fanxing.factory.dynaFactory.Cls2");
i2.do2()
Cls1 i1 = Factory.getInstance("fanxing.factory.dynaFactory.Cls1");
i1.do1();
Cls2 i2 = Factory.getInstance("fanxing.factory.dynaFactory.Cls2");
i2.do2()
测试结果为:
cls1...
cls2...
需要注意的是,使用这种方法有几个问题:
第一, return (U) cls.newInstance();这个语句会有警告性的错误,好在这种错误不是编译性的错误,还是我们可以承受的。
第二, 编译器不能做类型检查,如Cls1 i1 = Factory.getInstance("fanxing.factory.dynaFactory.Cls1");这句,换成Cls2 i1 = Factory.getInstance("fanxing.factory.dynaFactory.Cls1");也是可以编译过去的。只是在运行的时候才会出错。
除了上面的方法,还有一种更好的方法。
这个方法需要我们在运行期内传入的对象是Class对象,当然是经过泛型的Class对象,就像下面的样子:
view plaincopy to clipboardprint?
Class<CLS1></CLS1> cls = Cls1.class;
try
{
Intf1 obj = cls.newInstance();
obj.do1();
}
catch(Exception e)
{
e.printStackTrace();
}
Class cls = Cls1.class;
try
{
Intf1 obj = cls.newInstance();
obj.do1();
}
catch(Exception e)
{
e.printStackTrace();
}
可以很清楚地看到,这里完全没有了强制类型转化,当然也就没有第一种方法所出现的那两个问题。
运行结果为:
cls1...
根据这个方法,我们可以把前面的初始化方法改造为:
view plaincopy to clipboardprint?
public static <U object="" extends=""> U getInstance(Class<U> cls)
{
try
{
return cls.newInstance();
}
catch(Exception e)
{
e.printStackTrace();