|
泛型與反射 研究泛型與反射之間的關(guān)系非常有趣。
我們知道,反射和泛型都是Java的一種動(dòng)態(tài)技術(shù)。而不像繼承和多態(tài),是面向?qū)ο蟮募夹g(shù)??梢赃@么說(shuō),反射和泛型都像是為了彌補(bǔ)像繼承和多態(tài)這些面向?qū)ο蠹夹g(shù)的不足而產(chǎn)生的。模式多是建立在面向?qū)ο蠹夹g(shù)基礎(chǔ)上的,因而每種模式多多少少在動(dòng)態(tài)性,或者說(shuō)擴(kuò)展性方面有些不足,我們就又結(jié)合了反射和泛型對(duì)模式進(jìn)行一定的擴(kuò)展,使它在動(dòng)態(tài)性方面更符合我們的要求。
在將這些技術(shù)結(jié)合起來(lái)的過(guò)程中,我們多多少少會(huì)想到將泛型和反射結(jié)合起來(lái)。這是非常有趣的話題:范型和反射都使得Java有了一定的擴(kuò)展性,但同時(shí)都有它自己的不足,而將這兩種技術(shù)結(jié)合起來(lái),是不是又能解決各自的不足,使得它們?cè)趧?dòng)態(tài)性上更上一層樓呢?
正像前面所說(shuō)的,泛型和反射可以相互促進(jìn),我們先來(lái)看看泛型是怎么幫助反射的。
我們知道,使用反射的一個(gè)最大的煩惱就是應(yīng)用反射以后得到的基本上都是Object類(lèi)型的對(duì)象,這種對(duì)象要使用,需要我們進(jìn)行強(qiáng)制類(lèi)型轉(zhuǎn)化。而我們更知道,泛型的功能之一就是消除我們使用強(qiáng)制類(lèi)型轉(zhuǎn)化。
1. 運(yùn)行期內(nèi)初始化對(duì)象
運(yùn)行期內(nèi)初始化對(duì)象是我們最常用的反射功能,但是我們通過(guò)反射在運(yùn)行期內(nèi)得到的對(duì)象的類(lèi)型通常是Object類(lèi)型的,而這個(gè)對(duì)象又需要我們?cè)谑褂玫臅r(shí)候進(jìn)行強(qiáng)制類(lèi)型轉(zhuǎn)化。現(xiàn)在,有了反射,可以使我們不需要做強(qiáng)制類(lèi)型轉(zhuǎn)化這個(gè)工作。
假設(shè)我們已經(jīng)有了兩個(gè)類(lèi):
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...");
}
}
我們需要在運(yùn)行期內(nèi)初始化這兩個(gè)對(duì)象,我們可以設(shè)計(jì)如下的初始化方法:
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;
}
}
}
在這個(gè)方法里,我們其實(shí)是利用泛型在初始化方法里提前做了強(qiáng)制類(lèi)型轉(zhuǎn)化的工作,這樣使得我們不必在使用的時(shí)候進(jìn)行強(qiáng)制類(lèi)型轉(zhuǎn)化的工作。
它們的測(cè)試代碼如下:
Cls1 i1 = Factory.getInstance("fanxing.factory.dynaFactory.Cls1");
i1.do1();
Cls2 i2 = Factory.getInstance("fanxing.factory.dynaFactory.Cls2");
i2.do2()
測(cè)試結(jié)果為:
cls1...
cls2...
需要注意的是,使用這種方法有幾個(gè)問(wèn)題:
第一, return (U) cls.newInstance();這個(gè)語(yǔ)句會(huì)有警告性的錯(cuò)誤,好在這種錯(cuò)誤不是編譯性的錯(cuò)誤,還是我們可以承受的。
第二, 編譯器不能做類(lèi)型檢查,如Cls1 i1 = Factory.getInstance("fanxing.factory.dynaFactory.Cls1");這句,換成Cls2 i1 = Factory.getInstance("fanxing.factory.dynaFactory.Cls1");也是可以編譯過(guò)去的。只是在運(yùn)行的時(shí)候才會(huì)出錯(cuò)。
除了上面的方法,還有一種更好的方法。
這個(gè)方法需要我們?cè)谶\(yùn)行期內(nèi)傳入的對(duì)象是Class對(duì)象,當(dāng)然是經(jīng)過(guò)泛型的Class對(duì)象,就像下面的樣子:
Class<Cls1> cls = Cls1.class;
try
{
Intf1 obj = cls.newInstance();
obj.do1();
}
catch(Exception e)
{
e.printStackTrace();
}
可以很清楚地看到,這里完全沒(méi)有了強(qiáng)制類(lèi)型轉(zhuǎn)化,當(dāng)然也就沒(méi)有第一種方法所出現(xiàn)的那兩個(gè)問(wèn)題。
運(yùn)行結(jié)果為:
cls1...
根據(jù)這個(gè)方法,我們可以把前面的初始化方法改造為:
public static <U extends Object> U getInstance(Class<U> cls)
{
try
{
return cls.newInstance();
}
catch(Exception e)
{
e.printStackTrace();
return null;
}
}
我們來(lái)進(jìn)行以下測(cè)試:
Cls1 c1 = Factory.getInstance(Cls1.class);
c1.do1();
測(cè)試結(jié)果為:
cls1...
這時(shí)候,如果我們將上面的測(cè)試代碼改為:
Cls2 c1 = Factory.getInstance(Cls1.class);
就會(huì)出現(xiàn)編譯錯(cuò)誤,可以看到,我們的第二種方法的確避免了第一種方法的弱點(diǎn),但第一種方法的好處是,只需要往初始化方法里輸入類(lèi)名作為參數(shù)。
2. 運(yùn)行期內(nèi)調(diào)用方法
使用過(guò)反射的人都知道,運(yùn)行期內(nèi)調(diào)用方法后得到的結(jié)果也將是一個(gè)Object對(duì)象。這樣的結(jié)果在一般的方法反射上也就是可以忍受的了。但是有時(shí)候也是不能忍受的,比如,我們想反射List<T>類(lèi)的iterator()方法。
一般的,我們使用反射可以這么做:
try
{
Class<?> cls = Class.forName("java.util.ArrayList");
Method m = cls.getDeclaredMethod("iterator",new Class[0]);
return m.invoke(this,new Object[0]);
}
catch(Exception e)
{
e.printStackTrace();
return null;
}
當(dāng)然,這里返回的是一個(gè)Object對(duì)象,而List<T>類(lèi)的iterator()的實(shí)際返回類(lèi)型為T(mén)。很明顯,我們可以將上面的代碼做如下的修改:
try
{
Class<?> cls = Class.forName("java.util.ArrayList");
Method m = cls.getDeclaredMethod("iterator",new Class[0]);
return (T)m.invoke(this,new Object[0]);
}
catch(Exception e)
{
e.printStackTrace();
return null;
}
同樣,我們的代碼也會(huì)遇到警告性的錯(cuò)誤。但是我們可以置之不理。
3. 運(yùn)行期內(nèi)初始化數(shù)組對(duì)象
同樣,在運(yùn)行期內(nèi)初始化得到的數(shù)組也是一個(gè)Object對(duì)象。就像下面的樣子:
Object o = Array.newInstance(int.class,10);
如果我想在getArray()方法里得到數(shù)組對(duì)象,就會(huì)得到像下面這個(gè)樣子的代碼:
public Object getArray(Class cls,int size)
{
return Array.newInstance(cls,size);
}
這顯然不會(huì)令我們滿意。因?yàn)槿绻覀冚斎胍粋€(gè)Class<T>類(lèi)型的參數(shù),就希望返回一個(gè)T類(lèi)型的結(jié)果。
在這種想法下,我們就使用泛型將getArray()方法做如下修改:
public T[] getArray(Class<T> cls,int size)
{
return (T[])Array.newInstance(cls,size);
}
這樣的修改,將使我們得到一個(gè)比較滿意的結(jié)果。同樣,上面的代碼也會(huì)得到一個(gè)警告性的錯(cuò)誤。但是,使用泛型使我們的結(jié)果得到了很大的優(yōu)化。
上面的幾個(gè)例子讓我們看到了泛型能夠幫助反射,而且是大大優(yōu)化了反射的結(jié)果。但在同時(shí),反射也不是被動(dòng)的接受泛型的幫助,反射同樣也可以幫助泛型。這是基于反射的基本工作原理:得到數(shù)據(jù)的元數(shù)據(jù)。也就是說(shuō),可以通過(guò)反射得到泛型的元數(shù)據(jù)。除此之外,反射也有利于幫助泛型數(shù)據(jù)運(yùn)行期內(nèi)初始化。
下面以一兩個(gè)簡(jiǎn)單例子加以說(shuō)明。
4. 使用反射初始化泛型類(lèi)
我們通常會(huì)在一個(gè)類(lèi)里這樣使用泛型:
public final class Pair<A,B> {
public final A fst;
public final B snd;
public Pair(A fst, B snd) {
this.fst = fst;
this.snd = snd;
}
……
}
這當(dāng)然是我們最基本的用法,但常常會(huì)這樣:編譯器希望知道更多的關(guān)于這個(gè)未知對(duì)象如A fst的信息,這樣,我們可以在運(yùn)行期內(nèi)調(diào)用某一些方法。大家說(shuō)啊,這很容易啊,我們可以把這種未知類(lèi)型作為參數(shù)輸入。呵呵,這就對(duì)了,有了這樣參數(shù),下一步,我們就要使用反射在運(yùn)行期內(nèi)調(diào)用它的方法。
關(guān)于這樣的例子,我在這里不再給出。我在這里給出一個(gè)簡(jiǎn)單一些的例子,就是對(duì)泛型類(lèi)初始化需要調(diào)用的構(gòu)造器。
對(duì)于上面的Pair<A,B>類(lèi),如果構(gòu)造器的輸入?yún)?shù)的類(lèi)型不是A和B,而是Class<A>和Class<B>,那么我們就不得不在構(gòu)造器里使用反射了。
public Pair(Class<A> typeA, Class<B> typeB) {
this.fst = typeA.newInstance();
this.snd = typeB.newInstance();
……
}
由此可見(jiàn),對(duì)于泛型里的未知類(lèi)型參數(shù),我們也完全可以和普通類(lèi)型一樣使用反射工具。即可以通過(guò)反射對(duì)這些未知類(lèi)型參數(shù)做反射所能做到的任何事情。
5. 使用反射得到泛型信息
關(guān)于這一個(gè)小結(jié)的問(wèn)題,顯得更加得有趣。
我們還是以上面的Pair<A,B>作為例子,假如我們?cè)谝粋€(gè)類(lèi)中使用到了這個(gè)類(lèi),如下所示:
public Class PairUser
{
private Pair<String,List> pair;
……
}
如果我們對(duì)PairUser類(lèi)應(yīng)用反射,我們可以很輕松的得到該類(lèi)的屬性pair的一些信息,如它是private還是public的,它的類(lèi)型等等。
如果我們通過(guò)反射得到pair屬性的類(lèi)型為Pair以后,我們知道該類(lèi)是一個(gè)泛型類(lèi),那么我們就想進(jìn)一步知道該泛型類(lèi)的更進(jìn)一步的信息。比如,泛型類(lèi)的類(lèi)名,泛型參數(shù)的信息等等。
具體到上面的PairUser類(lèi)的例子,我現(xiàn)在想知道它的屬性pair的一些信息,比如,它的類(lèi)型名、泛型參數(shù)的類(lèi)型,如String和List等等這些信息。所要做的工作如下:
首先是取得這個(gè)屬性
Field field = PairUser.class.getDeclaredField("pair");
然后是取得屬性的泛型類(lèi)型
Type gType = field.getGenericType();
再判斷gType是否為ParameterizedType類(lèi)型,如果是則轉(zhuǎn)化為ParameterizedType類(lèi)型的變量
ParameterizedType pType = (ParameterizedType)gType;
取得原始類(lèi)型
Type rType = pType.getRawType();
然后就可以通過(guò)rType.getClass().getName()獲得屬性pair的類(lèi)型名。
最后獲取參數(shù)信息
Type[] tArgs = pType.getActualTypeArguments();
可以通過(guò)tArgs[j].getClass().getName()取得屬性pair的泛型參數(shù)的類(lèi)型名。
完整的代碼如下:
try
{
Field field = PairUser.class.getDeclaredField("pair");
Type gType = field.getGenericType();
if(gType instanceof ParameterizedType)
{
ParameterizedType pType = (ParameterizedType)gType;
Type rType = pType.getRawType();
System.out.println("rawType is instance of " +
rType.getClass().getName());
System.out.println(" (" + rType + ")");
Type[] tArgs = pType.getActualTypeArguments();
System.out.println("actual type arguments are:");
for (int j = 0; j < tArgs.length; j++) {
System.out.println(" instance of " +
tArgs[j].getClass().getName() + ":");
System.out.println(" (" + tArgs[j] + ")");
}
}
else
{
System.out.println("getGenericType is not a ParameterizedType!");
}
}
catch(Exception e)
{
e.printStackTrace();
}
}
輸出結(jié)果為:
rawType is instance of java.lang.Class
(class Pair)
actual type arguments are:
instance of java.lang.Class:
(class java.lang.String)
instance of java.lang.Class:
(interface java.util.List)
|
|
|
來(lái)自: 昵稱(chēng)2807 > 《Java or Jsp》