Java語言的反射功能是我們不得不使用的功能,但事實上它的使用是相當繁瑣卻在功能上比較單一的一個功能。
例如,我們使用Java語言反射最多的地方是在運行時對POJO對象或者Domain對象的“set”和“get”方法的調用,因為對象屬性是私有的,獲取和設置對象屬性的值是通過對應的“get”和“set”方法進行的。下面是一個關於“get”方法調用的例子:
public static Object getFieldValue(Object bean,Field field)
{
try
{
String type = field.getType().getName();
if(type.equals("boolean")||type.equals("java.lang.Boolean"))
{
Method m1 = bean.getClass().getMethod(StrUtil.getIsMethodName(field.getName()),null);
return m1.invoke(bean, null);
}
else
{
Method m1 = bean.getClass().getMethod(StrUtil.getGetMethodName(field.getName()),null);
return m1.invoke(bean, null);
}
}
catch(Exception e)
{
e.printStackTrace();
if(logger.isDebugEnabled())
{
logger.debug("getFieldValue", e);
}
returnnull;
}
}
上面的代碼首先判斷Field的屬性是否為boolean,如果是,則調用isXXXX()方法;否則調用getXXXX()方法。
在調用的時候,首先Field的name獲取對應的“is”或“get”方法名,然後獲得Method對象,最後調用該對象的invoke方法獲得返回值。
整個過程相當的繁瑣。
但Groovy語言中,我們設置或獲取對象的屬性值可以直接通過“對象名.屬性名”獲取,因此設置和獲取對象屬性的值就不直接通過“set”和“get”方法了。
上面的Java代碼在Groovy程序中可以改造成下面的代碼:
defstatic getFieldValue(Object bean,String fieldName)
{
bean."$fieldName"
}
不錯,在前面的文章中,我曾經提出,Groovy語言的動態性跟Gstring有很大的關系,現在就可以看到,動態設置或獲取對象屬性值是通過Gstring對象來完成的。
真是簡單得不可思議,假如有如下一個Domain類:
class Man
{
String name
String age
String addr
}
我們使用該類來進行測試:
Man man = new Man()
man.name = 'Mike'
man.age = '22'
man.addr = 'Shenzhen'
println getFieldValue(man,'age')
打印結果為:
22
再做一個測試:
Man man = new Man()
man.name = 'Mike'
man.age = '22'
man.addr = 'Shenzhen'
man.metaClass.properties.each
{
println"property name: ${it.name}, property value: ${man."${it.name}"}"
}
結果為:
property name: class, property value: class base.Man
property name: addr, property value: Shenzhen
property name: age, property value: 22
property name: metaClass, property value: groovy.lang.MetaClassImpl@1749757[class base.Man]
property name: name, property value: Mike
可以看到,Groovy語言中設置或獲取對象屬性的值,根本不需要另寫一個類似“getFieldValue”這樣的方法,直接獲取就行了,簡單明了。
除了使用Gstring設置或獲取對象的屬性值,在運行時獲取方法的返回值也是通過Gstring完成的。請看下面的例子。
我們首先在上面的Man類中加入一個“toString”方法,如下:
class Man
{
String name
String age
String addr
public String toString()
{
"$name is $age year old, and live in $addr"
}
}
下面,我們就在運行時調用“toString”方法:
Man man = new Man()
man.name = 'Mike'
man.age = '22'
man.addr = 'Shenzhen'
def functionName = 'toString'
println man."${functionName}"()
可以看到,與動態設置或獲取屬性值唯一不同的是在Gstring對象後面跟了一個“()”。在Groovy語言中,Gstring對象後面如果沒有括號,表示調用的是屬性;有括號表示調用的是方法。
如果你將上面代碼的後一句改為如下代碼:
println man."${functionName}()"
則會拋出如下的Exception:
Exception in thread "main" groovy.lang.MissingPropertyException: No such property: toString() for class: base.Man
在Groovy語言中,這種動態調用對象方法的方法將會在很多地方派上用場。其中就有著名的委派模式。Java語言由於其語言的特點,很少使用委派模式。而面向對象的繼承卻在實踐中遇到了越來越多的問題。
在“使用組合代替繼承”口號越來越響亮的今天,委派技術將不可避免的越來越多的使用到。下面試著舉出一個例子來說明。
class Buyer
{
def borrow4Car()
{
println'borrow money for car'
}
def borrow4House()
{
println'borrow money for house'
}
}
class Bank
{
def borrowMoney(type)
{
Buyer buyer = new Buyer()
buyer."borrow4${type}"()
}
}
buyer向bank貸款,目的各不相同,有的是為了買車,有的是為了買房。如果bank要調查buyer貸款的目的,我們需要調用Bank類的“borrowMoney”方法,但Bank類本身不知道buyer貸款的目的,因此它需要將這個功能委派給Buyer類。
在Bank類的“borrowMoney”方法體內,我們可以看到先new了一個Buyer對象,然後調用該對象的方法,就完成了委派的過程。
buyer."borrow4${type}"()
可以看到,委派功能的完成,同樣是借助Gstring的動態特性,在運行時調用對象的方法。
下面來測試上面的代碼:
Bank bank = new Bank()
bank.borrowMoney('Car')
打印結果為:
borrow money for car