??? ??? 題按:做了幾年的java程序,卻很難說對基本的OO概念都有理解。即便每天都寫的代碼中,也很難說清楚其中到底運(yùn)用了怎樣的概念和思想。前日的考試中看到關(guān)于上溯造型與下溯造型的問題,就查了些資料,集中說一下吧。
??? ??? 先給出個(gè)例子,代碼如下:
package
?casting;
public
?
abstract
?
class
?Animal?{
????
public
?
abstract
?
void
?speak();
????
public
?
void
?eat(){
????????
//
悶頭吃,不做額外的事情
????}
}
package
?casting;
public
?
interface
?DoorGod?{
????
void
?guard();
}
package
?casting;
public
?
class
?Cat?
extends
?Animal?{
????@Override
????
public
?
void
?eat()?{
????????
try
?{
????????????Thread.sleep(
1000
);
????????}?
catch
?(InterruptedException?e)?{
????????????e.printStackTrace();
????????}
????????
super
.eat();
????}
????@Override
????
public
?
void
?speak()?{
????????System.out.println(
"
喵喵
"
);
????}
}
package
?casting;
public
?
class
?Dog?
extends
?Animal?
implements
?DoorGod{
????@Override
????
public
?
void
?speak()?{
????????System.out.println(
"
汪汪
"
);
????}
????
public
?
void
?guard()?{
????????
while
(
true
){
????????????System.out.println(
"
汪汪
"
);
????????}
????}
????
}
????
其中Animal為基類,定義speak和eat方法,eat方法給出了空實(shí)現(xiàn);
DoorGod為門神接口,定義了
guard方法來守護(hù)家門;
Cat為繼承Animal的子類,這里假定貓有挑食的習(xí)慣,在eat中要耽擱點(diǎn)時(shí)間看看伙食;Dog也為繼承Animal的子類,同時(shí)它實(shí)現(xiàn)了DoorGod接口來守護(hù)家門。??? ??? 先說說上溯造型(upcasting)。這個(gè)術(shù)語緣于繼承關(guān)系圖的傳統(tǒng)畫法:將基類至于頂部,而向下發(fā)展的就是派生類。根據(jù)上面的sample,我給出下面的一個(gè)小應(yīng)用:
package?casting;
public?class?Main?{
????public?static?void?upcasting(Animal?animal){
????????animal.speak();
????????animal.eat();
????}
????public?static?void?main(String[]?args)?{
????????Animal?dog1?=?new?Dog();
????????upcasting(dog1);
????????
????????Dog?dog2?=?new?Dog();
????????upcasting(dog2);
????}
}
??? ??? 由于upcasting(Animal
animal)方法的參數(shù)是
Animal類型的,因此如果傳入的參數(shù)是
Animal的子類,傳入的參數(shù)就會(huì)被轉(zhuǎn)換成父類Animal類型,這樣你創(chuàng)建的Dog對象能使用的方法只是Animal中的簽名方法;也就是說,在上溯的過程中,Dog的接口變窄了,它本身的一些方法(例如實(shí)現(xiàn)了
DoorGod的guard方法)就不可見了。如果你想使用Dog中存在而Animal中不存在的方法(比如guard方法),編譯時(shí)不能通過的。由此可見,上溯造型是安全的類型轉(zhuǎn)換。另一方面,雖然upcasting(Animal
animal)方法的參數(shù)是
Animal類型,但傳入的參數(shù)可以是Animal的派生類(這也是OO編程中慣用的編程方法),這里面就有個(gè)對象的類型識(shí)別問題,也就是運(yùn)行時(shí)類型識(shí)別(run-time
type identification,縮寫為RTTI)
,這也可以單獨(dú)寫一篇文章了,《Thinking
in Java》中的第10章詳細(xì)地闡述了RTTI。
??? ??? 相對于類型轉(zhuǎn)換安全的上溯造型,下溯造型就未必是安全的了。我們經(jīng)常會(huì)做些強(qiáng)制類型轉(zhuǎn)換的事情,有時(shí)我們也會(huì)無意間遇到
ClassCastException的轉(zhuǎn)換異常(從這一點(diǎn)來說,我們應(yīng)該多用范型來避免不安全的類型轉(zhuǎn)換)。例如:
public?static?void?downcasting(Animal?animal){
????????//DoorGod?doorGod?=?(DoorGod)animal;
????????if(animal?instanceof?DoorGod){
????????????DoorGod?doorGod?=?(DoorGod)animal;
????????????doorGod.guard();
????????}
????????if(animal?instanceof?Cat){
????????????Cat?cat?=?(Cat)animal;
????????????cat.speak();
????????}
} ???
如果沒有采取措施(上面使用的措施是instanceof)判斷對象的類型,那么向下的強(qiáng)制轉(zhuǎn)換就是不安全的。這種轉(zhuǎn)換錯(cuò)誤在編譯時(shí)是不能檢測出來的,只有在運(yùn)行時(shí)才會(huì)拋出
ClassCastException異常,對于測試來說,這樣的錯(cuò)誤也是很難檢測的。
總的來說,上溯造型與下溯造型概念上還是很簡單的。但即便最簡單的東西,我們也有可能犯下錯(cuò)誤。用了那么多的框架后,回頭看看基礎(chǔ)知識(shí),發(fā)現(xiàn)自己的根基并不是想象中的牢固阿。
注:本文參考了《Thinking
in Java》,這本書真需要細(xì)細(xì)品味啊!