多態(tài)
多態(tài)(也稱作動態(tài)綁定、后期綁定或運行時綁定)通過分離做什么和怎么做,從另一角度將接口和實現(xiàn)分離開來。多態(tài)不但能夠改善代碼的組織結構和可讀性,還能夠創(chuàng)建可擴展的程序——即無論在項目最初創(chuàng)建時還是在需要添加新功能時都可以“生長”的程序。
8.1再論向上轉型
把某個對象的引用視為對其基類型的引用的做法被稱作向上轉型。
8.2轉機
將一個方法調用同一個方法主體關聯(lián)起來被稱作綁定。
后期綁定指在運行時根據(jù)對象的類型進行綁定。
Java中除了static方法和final方法(private方法屬于final方法)之外,其他所有的方法都是后期綁定。
當發(fā)送消息給某個對象,該對象會斷定應該做什么事(產生正確的行為)。
多態(tài)的特性:我們所做的代碼修改,不會對程序中其他不應受到影響的部分產生破壞。換句話說,多態(tài)是一項讓程序員“將改變的事物與未變的事物分離開來”的重要技術。
8.3構造器和多態(tài)
- 復雜對象構造器調用順序:
1、調用基類構造器
2、按聲明順序調用成員的初始化方法
3、調用導出類構造器的主體 - 在清理一個對象時,必須先對導出類進行清理,然后才是基類。這是因為導出類的清理可能會調用基類中的某些方法,所以需要使基類中的構件仍器作用而不應過早地銷毀它們。
- 如果在一個構造器內部調用正在構造的對象的某個動態(tài)綁定方法,將可能會招致災難:如果構造器只是在構建對象過程中的一個步驟,并且該對象所屬的類時從這個構造器所屬的類導出的,那么導出部分在當前構造器正在被調用的時刻仍舊是沒有初始化的。然而一個動態(tài)綁定的方法調用卻會向外深入到繼承層次結構內部,它可以調用導出類里的方法。而該方法所操縱的成員可能還未進行初始化。
示例:
class Glyph{
void draw(){print("Glyph.draw()");}
Gpyph(){draw();}
}
class RoundGlyph extends Glyph{
private int radius = 1;
RoundGlyph(int r){radius=r;}
void draw(){print("RoundGlyph.draw(),radius="+radius);}
}
public class PolyConstructors{
public static void main(String[] args){ new RoundGlyph(5);}
}
輸出結果為"RoundGlyph.draw(), radius=0"。其中radius不是初始值1,而是0。
8.4協(xié)變返回類型
表示在導出類中的被覆蓋方法可以返回基類方法的返回類型的某種導出類型。
class Grain{
public String toString(){return "Grain";}
}
class Wheat extends Grain{
public String toString{return "Wheat";}
}
class Mill{
Grain process(){return new Grain();}
}
class WheatMill extends Mill{
Wheat process(){return new Wheat();}
}
8.5用繼承進行設計
在使用現(xiàn)成類建立新類時,應首先考慮組合。組合不會強制我們的程序設計進入繼承的層次結構中。而且組合更加靈活,因為它可以動態(tài)選擇類型;相反,繼承在編譯時就需要知道確切類型。
示例:
class Actor{//...}
class HappyActor extends Actor{//...}
class SadActor extends Actor{//..}
class Stage{
private Actor actor = new HappyActor();
public void change(){actor = new SadActor();}
public void performPlay(){actor.act();}
}
public class Transmogrify{
public static void main(String[] args){
Stage stage= new Stage();
stage.performPlay();
stage.change();
stage.performPlay();
}
}
純繼承與擴展: is-a 與 is-like-a
is-like-a:導出類有著相同的基本接口,但是它還具有由額外方法實現(xiàn)的其他特性。缺點:導出類中接口的擴展部分不能被基類訪問,因此,一旦向上轉型,就不能調用那些新方法。
向上轉型會丟失具體的類型信息,向下轉型能夠或缺類型信息。在Java中,所有轉型都會得到檢查。
在運行期間對類型進行檢查的行為稱作“運行時類型識別”RTTI
class Useful{
public void f(){}
}
class MoreUseful{
public void f(){}
public void u(){}
}
public class RTTI{
public static void main(String[] args){
Useful[] x = {new Useful(), new MoreUseful()};
x[0].f();
x[1].f();
//x[1].u(); 編譯錯誤
((MoreUseful)x[1]).u(); //向下轉型
((MoreUseful)x[0]).u();//拋出異常
}
}
總結
多態(tài)意味著“不同的形式”。在面向對象的程序設計中,我們持有從基類繼承而來的相同接口,以及使用該接口的不同形式:不同版本的動態(tài)綁定方法。