參考3什麼是物件導向(3):Polymorphism
參考4Java 快速導覽 - 物件導向概念 多型
參考5劉逸-論物件導向part 5:Polymorphism
參考6程式設計俱樂部-OOP 中對 "多型" 的理解
參考7歐萊禮-多型
這裡主要是討論OOP多型的概念,爬了些資料自己做些整理。
首先看到參考1良葛格所寫,它把多型想成「is-a 關係」(是一種關係)來看,讓初學者很容易初步的理解多型的概念,與什麼時候的多型寫法會編譯失敗
一般來說這你一定看得懂例1:
SwordsMan swordsMan = new SwordsMan();
那這呢?例2
Role role1 = new SwordsMan();Role role2 = new Magician();
SwordsMan和Magician都繼承了Role,所以它兩都是一種Role
OK所以這兩行可編譯可執行SwordsMan swordsMan = new Role();
這會失敗,因為Role不一定是一種swordsMan
Role role2 = new Magician();
SwordsMan swordsMan = (SwordsMan) role2;
我們先來看第一行,我自己的解釋,宣告一個Role型態的變數role2,也是參考名稱,role2參考Magician的instance。
第二行,宣告一個SwordsMan型態的變數swordsMan,並且把Role型態的role2轉型成SwordsMan型態。
這個(SwordsMan)寫法叫型態轉換,分UpCasting(向上轉型)和DownCasting(向下轉型),子類別subclass轉父類別superclass是UpCasting,反之就是DownCasting。
所以這個例子Role是父、SwordsMan是子,把role2轉SwordsMan就是DownCasting。
而通常DownCasting會比較不容易,所以這兩行會編譯成功但執行失敗。
白話講,role2要扮演SwordsMan型態,所以它扮演成功,系統誤認,編譯成功但執行失敗。
你可能想問,為何執行失敗?因為Role不一定是一種swordsMan!
多型可以幹嘛?好處?
來看參考1良葛格中的例5:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 |
一個訊息(message or event or stimulus)的意義是由接收者(接收到這個訊息的物件)來解釋,而不是由訊息發出者(sender)來解釋。所以,在runtime時只要接受者換成不同的物件或是instance,系統的行為就會改變。具有這樣的特性就稱之為polymorphism。例5完全可以印證這個解釋!!
參考3中也有一個例子也來看看例6:
List<String> list = new LinkedList<String>();
list.add(“This is a book”);
寫程式的人送了一個add這個訊息給list這個instance,如果list這個instance不是像上面程式一樣寫死new LinkedList<String>();而是runtime時傳入的任意符合List介面的某個instance,那麼程式的行為就無法只從靜態的程式碼得知了,而且add(“This is a book”);訊息的解釋也只能看runtime時list instance指到哪一個instance而定。
看到這,多少對多型有一點概念了吧,我們繼續看下去~
在參考5中作者直接定義了多型:
例9,詳細原始碼請看參考4:
puppy1 、 puppy2 與 puppy3 被宣告為 Animal 型態,但其後以 Elephant 型態的建構子建立物件,這個意思是說,對父類別, 也就是 Animal 型態的參考變數,實際可以由子類別,也就是 Elephant 型態來建立物件。
如想要專業一點的解釋,應該就是這樣了吧,例4只是我不負責任解釋QQ
參考2,例10:
就定義面而言多型大多定義如下:將相同的訊息傳遞給不同的物件,進而引發出不同的行為反應。而就程式面的實做部分,我常將多型定義如下:來看他舉的例子例7:
- 利用父類別的型態
- 接受子類別的物件
- 做相同的動作
- 引發不同的行為
1 2 3 4 5 | public void { a. ; a. ; a. ; } |
以後只要:注意此method和例5的method 參數,都是一個父類別型態的參數,他也寫道多型起手式:
Tiger t = new Tiger;
Show(t); à這樣就會變成老虎走路、發出叫聲、最後跳火圈囉
『Inheritance』 +『Overriding』+『Upcasting』。多型伴隨著一定是有父子類別的關係,或介面跟實做的關係,藉由overriding子類別會將父類別的方法重新定義來符合自身所需;另一方面,我們會針對上層的抽象類別或介面來撰寫邏輯上的運作,避免這些邏輯運作跟實體的子類別產生關聯,如此對日後的維護將有大大的幫助,以上述的馬戲團為例:show()這個函式都是針對上層提供的方法來操作,但於執行期間系統會自動依據他所接收的Animal a此一物件所屬的類別來動態繫結show當中各邏輯運作所應該對應的方法為何。開始來多看幾個例子例8,參考1中的例子:
SwordsMan swordsMan2 = new SwordsMan();第三行(SwordsMan) role ,其實就是DownCasting。
Role role = swordsMan2; // SwordsMan是一種Role,這行通過編譯
// 你告訴編譯器要讓Role扮演SwordsMan,以下這行通過編譯
SwordsMan swordsMan = (SwordsMan) role; // role參考SwordsMan實例,執行成功
例9,詳細原始碼請看參考4:
1 2 3 4 | Animal puppy1, puppy2, puppy3; puppy1 = new Elephant("大象", 6, 70); puppy2 = new Elephant(); puppy3 = new Elephant("林旺", 88, 5000); |
如想要專業一點的解釋,應該就是這樣了吧,例4只是我不負責任解釋QQ
參考2,例10:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 |
Unspecifiedrun:
swim
Fly
再引用一段參考6中我覺得不錯,又有點專業的說法:
多型是物件導向機制中一個很重要的《物件結構化》觀念。
一般在OOA、OOD的過程中,會發展出一些功能或範疇相近的類別,它們有類似的行為與功能,但卻不適合塞近同一個類別中。這時為了規範開發原則,常會利用一個介面或基礎類別來延伸這些子類別,以強制這些子類別有完全一樣的屬性與方法,達到規格最佳化的目的。
參考7中寫道:
「當某變數的實際型態(actual type)和形式型態(formal type)不一致時,呼叫此變數的 method,一定會呼叫到「正確」的版本, 也就是實際型態的版本。並且在多型中注意兩點:
這裡開始來說明一下什麼是實際型態、形式型態
- 多型的機制只用在 method 上,不用在 field 上。
- 多型的機制只用在 instance method上,不用在 class method 上。
int a = 3;
long b =a;
對b來說它的實際和形式型態都一樣,因為它不是一種reference 型態。
例11:
來看21行,它會呼叫哪個method1?答:會叫ClassB的,因為20行兩邊型態不一致,這時就看實際型態了,所以它會叫ClassB()這實際型態的method1。
看22行,它會叫ClassA的method2,這裡我覺得是因為ClassB繼承ClassA了,所以ClassB也擁有method2,所以當然就可以呼叫。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 |
看22行,它會叫ClassA的method2,這裡我覺得是因為ClassB繼承ClassA了,所以ClassB也擁有method2,所以當然就可以呼叫。
23行,編譯錯誤,因為a是ClassA的形式型態,而ClassA沒有method3,雖然ClassB有,但編譯時只看形式型態,而不管實際型態,所以這時就會編譯錯誤!
故形式型態也稱編譯期型態(compile-time type)實際型態稱執行期型態(runtime type)
例12,配合例11看:
1 2 3 4 5 6 7 8 9 10 11 | public classE { public static void { ClassB b = new ClassB(); ClassD. ; } } |
再來,多型的機制只用在 method 上,不用在 field 上 好處:
- 有直接或間接繼承關係的兩個類別,子類別的 method 將父類別的 method 予以override。
- 實際型態為子類別的物件,被當成父類別來使用,呼叫其 overrided method。
- 繼承程式碼:達到程式碼再用性(reuse)
- 繼承介面(interface)。達到介面再用性(reuse),為多型預作準備
例13:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 |
沒有留言:
張貼留言