Java對(duì)象類(lèi)型轉(zhuǎn)換的四個(gè)經(jīng)驗(yàn)
一、向上轉(zhuǎn)型與向下轉(zhuǎn)型。
對(duì)象類(lèi)型的轉(zhuǎn)換在Java語(yǔ)言平臺(tái)中經(jīng)常遇到,主要包括向上轉(zhuǎn)型與向下轉(zhuǎn)型操作。程序開(kāi)發(fā)人員需要熟練掌握這兩個(gè)轉(zhuǎn)型的方法以及其中容易出錯(cuò)的地方。如何來(lái)了解這兩個(gè)轉(zhuǎn)型的區(qū)別呢?筆者認(rèn)為,以一個(gè)現(xiàn)實(shí)的例子作為比喻,可能會(huì)更加的容易理解。
如現(xiàn)在有動(dòng)物、鳥(niǎo)類(lèi)、燕子三個(gè)名詞,他們之間有什么關(guān)系呢?通常我們都會(huì)說(shuō),燕子是特殊的鳥(niǎo)類(lèi),或者說(shuō)燕子是鳥(niǎo)類(lèi)的一種。為此,從對(duì)象的定義來(lái)看,鳥(niǎo)類(lèi)就是一個(gè)父類(lèi),而燕子就是一個(gè)子類(lèi)?;蛘哒f(shuō),燕子對(duì)象就是一個(gè)鳥(niǎo)類(lèi)對(duì)象。筆者這里要強(qiáng)調(diào)的一點(diǎn)就是,由于燕子是鳥(niǎo)類(lèi)的一個(gè)對(duì)象,所以鳥(niǎo)類(lèi)所具有的特性燕子全部具有。而燕子所具有的特性(如遷徙)則鳥(niǎo)類(lèi)不一定都具有。在這個(gè)例子中,燕子也是一種鳥(niǎo)類(lèi)。為此可以將燕子的對(duì)象堪稱(chēng)是一個(gè)鳥(niǎo)類(lèi)的對(duì)象。這種方法在Java語(yǔ)言環(huán)境中就叫做“向上轉(zhuǎn)型”。從這個(gè)例子中可以看出,向上轉(zhuǎn)型是一個(gè)從較抽象類(lèi)型的類(lèi)(鳥(niǎo)類(lèi))向比較具體的類(lèi)(燕子)過(guò)度。由于具體類(lèi)(燕子)具有抽象類(lèi)(鳥(niǎo)類(lèi))的全部特性,所以在這個(gè)轉(zhuǎn)換過(guò)程中是不會(huì)有問(wèn)題的。這就好像一個(gè)邏輯判斷題說(shuō)燕子是鳥(niǎo)類(lèi)的一種,其具有鳥(niǎo)類(lèi)的全部特性。這個(gè)命題至少到現(xiàn)在為止是完全正確的。
但是,在實(shí)際工作中,我們還經(jīng)常會(huì)遇到向下轉(zhuǎn)型的情況。也就是說(shuō)從一個(gè)抽象類(lèi)中(鳥(niǎo)類(lèi))引用具體類(lèi)(燕子)中的對(duì)象。也就是說(shuō),我們可以說(shuō)燕子是鳥(niǎo)類(lèi)的一種。但是現(xiàn)在反過(guò)來(lái),如果說(shuō)鳥(niǎo)類(lèi)就是燕子,那顯然就是以偏概全了,因?yàn)檠嘧硬⒉痪哂衅渌B(niǎo)類(lèi)的特性。如鴿子的特性燕子就沒(méi)有。所以,在應(yīng)用程序開(kāi)發(fā)中,如果將父類(lèi)對(duì)象賦值給子類(lèi)的對(duì)象,就可能有問(wèn)題。如果硬要這么做的話(huà),則很有可能發(fā)生編譯器錯(cuò)誤。因?yàn)楦割?lèi)對(duì)象并不一定是子類(lèi)的實(shí)例。這是什么意思呢?即所說(shuō)的鳥(niǎo)類(lèi)(父類(lèi)對(duì)象)并不一定是子類(lèi)對(duì)象(燕子)。因?yàn)轼B(niǎo)類(lèi)對(duì)象還有可能是鴿子、白鷺等等。所以,如果將父類(lèi)對(duì)象給子類(lèi)對(duì)象的話(huà),那么就會(huì)出現(xiàn)問(wèn)題。
二、如何實(shí)現(xiàn)向下轉(zhuǎn)型?
由于向上轉(zhuǎn)型一般都是安全的,即將一個(gè)子類(lèi)對(duì)象直接賦值給父類(lèi)對(duì)象,一般被認(rèn)為是安全的,如燕子是鳥(niǎo)類(lèi)在哪里都是成立的。所以在向下轉(zhuǎn)型時(shí)不需要采用其他的關(guān)鍵字,我們常常把向下轉(zhuǎn)換叫做隱式轉(zhuǎn)換。但是在這里向上轉(zhuǎn)換是一種不安全的轉(zhuǎn)換方式,如說(shuō)鳥(niǎo)類(lèi)就是燕子,這種說(shuō)法無(wú)論在哪里都說(shuō)不過(guò)去。為此默認(rèn)情況下,進(jìn)行向下轉(zhuǎn)型時(shí),往往會(huì)發(fā)生編譯器錯(cuò)誤。
一般情況下,越是具體的對(duì)象所具有的特性越多。如燕子的特性就比鳥(niǎo)類(lèi)的特性多的多。而越抽象的對(duì)象反而具有的特性越少,因?yàn)槠渲痪哂幸恍┏橄髮?duì)象的共性特征。在進(jìn)行向下轉(zhuǎn)型操作時(shí),將特性范圍小的對(duì)象轉(zhuǎn)換為特性范圍大的對(duì)象肯定會(huì)出現(xiàn)問(wèn)題。為此在向下轉(zhuǎn)型時(shí),必須確保轉(zhuǎn)換后不會(huì)出現(xiàn)問(wèn)題,即具體對(duì)象的特性在抽象對(duì)象中也全部具備,只有如此才能夠進(jìn)行轉(zhuǎn)換。而且即使?jié)M足這個(gè)條件,編譯器也不不能夠進(jìn)行隱式轉(zhuǎn)換。而是需要采用關(guān)鍵字進(jìn)行強(qiáng)制轉(zhuǎn)換。如子類(lèi)對(duì)象名字=(子類(lèi)名)父類(lèi)對(duì)象名字。如果上面這個(gè)語(yǔ)法,就可以實(shí)現(xiàn)對(duì)象類(lèi)型的強(qiáng)制轉(zhuǎn)換。
筆者在此強(qiáng)調(diào)一遍,在進(jìn)行向下轉(zhuǎn)型時(shí)一定要進(jìn)行強(qiáng)制轉(zhuǎn)換。即通過(guò)子類(lèi)對(duì)象名字=(子類(lèi)名)父類(lèi)對(duì)象名字進(jìn)行賦值,而不能夠向向上轉(zhuǎn)型那樣進(jìn)行隱式轉(zhuǎn)換。
三、確保向下轉(zhuǎn)型的準(zhǔn)確性。
從以上分析中可以看出,向下轉(zhuǎn)型往往被認(rèn)為是不安全的。當(dāng)在程序中執(zhí)行向下轉(zhuǎn)型操作的時(shí)候,如果父類(lèi)對(duì)象不是子類(lèi)對(duì)象的實(shí)例,就會(huì)發(fā)生編譯器錯(cuò)誤。所以在執(zhí)行向下轉(zhuǎn)型之前要先作一件事情,就是判斷父類(lèi)對(duì)象是否為子類(lèi)對(duì)象的實(shí)例。也就是說(shuō),先要想一想,燕子就是鳥(niǎo)類(lèi)這個(gè)命題是否成立(在某些特定的情況下這個(gè)偽命題可能會(huì)成立,如燕子的特性與鳥(niǎo)類(lèi)的特性完全一致)。只有如此,向下轉(zhuǎn)型才不會(huì)出現(xiàn)問(wèn)題。在進(jìn)行向下轉(zhuǎn)型操作時(shí),將特性范圍小的對(duì)象轉(zhuǎn)換為特性范圍大的對(duì)象肯定會(huì)出現(xiàn)問(wèn)題。但是,如果兩個(gè)轉(zhuǎn)換的對(duì)象特性范圍一樣大的話(huà),可那么就不會(huì)有問(wèn)題了。
在應(yīng)用程序開(kāi)發(fā)中,往往通過(guò)操作符instanceof來(lái)完成這個(gè)判斷。即可以利用這個(gè)操作符來(lái)判斷是否一個(gè)雷實(shí)現(xiàn)了某個(gè)接口,也可以用來(lái)判斷一個(gè)實(shí)例對(duì)象是否屬于一個(gè)類(lèi)。這個(gè)操作符的基本格式為:A(某個(gè)類(lèi)的對(duì)象引用) instanceo(操作符號(hào)) B (某個(gè)類(lèi)的名稱(chēng))。這個(gè)操作符***返回的是一個(gè)布爾值。如果是false的話(huà),則說(shuō)明A對(duì)象不是類(lèi)B的實(shí)例對(duì)象。相反,如果返回的值是true的話(huà),則說(shuō)明對(duì)象A是類(lèi)B的實(shí)例對(duì)象。
四、向下轉(zhuǎn)型的注意事項(xiàng)。
在進(jìn)行向下轉(zhuǎn)型時(shí),需要注意以下幾方面的內(nèi)容:
一是要慎用向下轉(zhuǎn)型。由于向下轉(zhuǎn)型容易出問(wèn)題,為此不到萬(wàn)不得已的時(shí)候,***不用使用向下轉(zhuǎn)型。條條道路通羅馬,如果在編程之前,合理規(guī)劃類(lèi),往往可以避免向下轉(zhuǎn)型的發(fā)生。只有其他路走不通的情況下,才考慮通過(guò)向下轉(zhuǎn)型的技術(shù)來(lái)解決問(wèn)題。
二是在進(jìn)行向下轉(zhuǎn)型的時(shí)候,需要做兩件事情。一是一定要使用instanceof操作符來(lái)判斷轉(zhuǎn)型的合法性,即判斷父類(lèi)對(duì)象是否為子類(lèi)對(duì)象的實(shí)例。這就好像在編寫(xiě)四則運(yùn)算時(shí),要判斷除數(shù)不為零一樣。這是必須要做的。也是程序員必須要養(yǎng)成的一個(gè)習(xí)慣。在進(jìn)行向下轉(zhuǎn)型時(shí),就自然而然會(huì)想到需要進(jìn)行這個(gè)判斷。只有如此,應(yīng)用程序的錯(cuò)誤才能夠降低。而且還能夠滿(mǎn)足不同的需求。二是需要注意向上轉(zhuǎn)型與向下轉(zhuǎn)型的區(qū)別。一般情況下,向上轉(zhuǎn)型往往被認(rèn)為是安全的,所以在Java語(yǔ)言平臺(tái)中向上轉(zhuǎn)型采用的是隱式轉(zhuǎn)型。而向下轉(zhuǎn)型由于特性范圍大小的不同,為此往往被認(rèn)為是不安全的。故系統(tǒng)默認(rèn)情況下進(jìn)行向下轉(zhuǎn)型時(shí)必須采用強(qiáng)制轉(zhuǎn)型的方式。如果不采用強(qiáng)制轉(zhuǎn)型,則即使?jié)M足向下轉(zhuǎn)型的條件,其也會(huì)發(fā)生編譯器錯(cuò)誤。所以需要切記,向下轉(zhuǎn)型必須要采用強(qiáng)制轉(zhuǎn)型。
三是需要做好備注等注釋工作。由于像向下轉(zhuǎn)型等操作是容易出現(xiàn)問(wèn)題的地方。為此在進(jìn)行類(lèi)似的操作時(shí),***在行注釋或者塊注釋中能夠進(jìn)行說(shuō)明。這對(duì)于后續(xù)的維護(hù)與代碼的升級(jí)是很有幫助的。好記性不如爛筆頭。如果沒(méi)有做好相關(guān)注釋的話(huà),這次可能沒(méi)有問(wèn)題,但是下次再代碼升級(jí)或者其他原因需要調(diào)整或者重寫(xiě)原有的代碼時(shí),就可能會(huì)因?yàn)槭韬龆鴮?dǎo)致轉(zhuǎn)型的失敗。
***筆者再次提醒各位程序員,向上轉(zhuǎn)型大家可以放心大膽的用。但是在使用向下轉(zhuǎn)型技術(shù)時(shí),大家要慎重,要按部就班(先判斷后使用)的進(jìn)行操作。
【編輯推薦】