通常
繼承最終會以創建一系列類收場
所有類都建立在統一的接口基礎上
我們用一幅顛倒的樹形圖來闡明這一點(見注釋)
[注釋] 這兒采用了
統一記號法
對這樣的一系列類
我們要進行的一項重要處理就是將衍生類的對象當作基礎類的一個對象對待
這一點是非常重要的
因為它意味著我們只需編寫單一的代碼
令其忽略類型的特定細節
只與基礎類打交道
這樣一來
那些代碼就可與類型信息分開
所以更易編寫
也更易理解
此外
若通過繼承增添了一種新類型
如
三角形
那麼我們為
幾何形狀
新類型編寫的代碼會象在舊類型裡一樣良好地工作
所以說程序具備了
擴展能力
具有
擴展性
以上面的例子為基礎
假設我們用Java寫了這樣一個函數
void doStuff(Shape s) {
s
erase();
//
s
draw();
}
這個函數可與任何
幾何形狀
(Shape)通信
所以完全獨立於它要描繪(draw)和刪除(erase)的任何特定類型的對象
如果我們在其他一些程序裡使用doStuff()函數
Circle c = new Circle();
Triangle t = new Triangle();
Line l = new Line();
doStuff(c);
doStuff(t);
doStuff(l);
那麼對doStuff()的調用會自動良好地工作
無論對象的具體類型是什麼
這實際是一個非常有用的編程技巧
請考慮下面這行代碼
doStuff(c);
此時
一個Circle(圓)句柄傳遞給一個本來期待Shape(形狀)句柄的函數
由於圓是一種幾何形狀
所以doStuff()能正確地進行處理
也就是說
凡是doStuff()能發給一個Shape的消息
Circle也能接收
所以這樣做是安全的
不會造成錯誤
我們將這種把衍生類型當作它的基本類型處理的過程叫作
Upcasting
(上溯造型)
其中
cast
(造型)是指根據一個現成的模型創建
而
Up
(向上)表明繼承的方向是從
上面
來的——即基礎類位於頂部
而衍生類在下方展開
所以
根據基礎類進行造型就是一個從上面繼承的過程
即
Upcasting
在面向對象的程序裡
通常都要用到上溯造型技術
這是避免去調查准確類型的一個好辦法
請看看doStuff()裡的代碼
s
erase();
//
s
draw();
注意它並未這樣表達
如果你是一個Circle
就這樣做
如果你是一個Square
就那樣做
等等
若那樣編寫代碼
就需檢查一個Shape所有可能的類型
如圓
矩形等等
這顯然是非常麻煩的
而且每次添加了一種新的Shape類型後
都要相應地進行修改
在這兒
我們只需說
你是一種幾何形狀
我知道你能將自己刪掉
即erase()
請自己采取那個行動
並自己去控制所有的細節吧
動態綁定 在doStuff()的代碼裡
最讓人吃驚的是盡管我們沒作出任何特殊指示
采取的操作也是完全正確和恰當的
我們知道
為Circle調用draw()時執行的代碼與為一個Square或Line調用draw()時執行的代碼是不同的
但在將draw()消息發給一個匿名Shape時
根據Shape句柄當時連接的實際類型
會相應地采取正確的操作
這當然令人驚訝
因為當Java編譯器為doStuff()編譯代碼時
它並不知道自己要操作的准確類型是什麼
盡管我們確實可以保證最終會為Shape調用erase()
為Shape調用draw()
但並不能保證為特定的Circle
Square或者Line調用什麼
然而最後采取的操作同樣是正確的
這是怎麼做到的呢?
將一條消息發給對象時
如果並不知道對方的具體類型是什麼
但采取的行動同樣是正確的
這種情況就叫作
多形性
(Polymorphism)
對面向對象的程序設計語言來說
它們用以實現多形性的方法叫作
動態綁定
編譯器和運行期系統會負責對所有細節的控制
我們只需知道會發生什麼事情
而且更重要的是
如何利用它幫助自己設計程序
有些語言要求我們用一個特殊的關鍵字來允許動態綁定
在C++中
這個關鍵字是virtual
在Java中
我們則完全不必記住添加一個關鍵字
因為函數的動態綁定是自動進行的
所以在將一條消息發給對象時
我們完全可以肯定對象會采取正確的行動
即使其中涉及上溯造型之類的處理
抽象的基礎類和接口 設計程序時
我們經常都希望基礎類只為自己的衍生類提供一個接口
也就是說
我們不想其他任何人實際創建基礎類的一個對象
只對上溯造型成它
以便使用它們的接口
為達到這個目的
需要把那個類變成
抽象
的——使用abstract關鍵字
若有人試圖創建抽象類的一個對象
編譯器就會阻止他們
這種工具可有效強制實行一種特殊的設計
亦可用abstract關鍵字描述一個尚未實現的方法——作為一個
根
使用
指出
這是適用於從這個類繼承的所有類型的一個接口函數
但目前尚沒有對它進行任何形式的實現
抽象方法也許只能在一個抽象類裡創建
繼承了一個類後
那個方法就必須實現
否則繼承的類也會變成
抽象
類
通過創建一個抽象方法
我們可以將一個方法置入接口中
不必再為那個方法提供可能毫無意義的主體代碼
interface(接口)關鍵字將抽象類的概念更延伸了一步
它完全禁止了所有的函數定義
接口
是一種相當有效和常用的工具
另外如果自己願意
亦可將多個接口都合並到一起(不能從多個普通class或abstract class中繼承)
From:http://tw.wingwit.com/Article/program/Java/hx/201311/25632.html