多態可以是客戶端使用統一的接口執行不同的行為。統一的接口是因為繼承類都覆蓋了基類方法,從而為自己定義了單獨的行為。“向上轉型”(upcasting,將繼承類對象賦給基類對象的一種行為)和動態編譯能夠確定執行哪段代碼。如下:
class Shape{
?void draw(){
? System.out.println("Shape.draw()");
?}
}
class Circle extends Shape{
?void draw(){
? System.out.println("Circle.draw()");
?}
}
public class PolyTest{
?public static void f(Shape s){
?s.draw();
}
?public static void main(String[] args){
? Circle c=new Circle();
? f(c);
?}
}
運行結果:
Circle.draw()
從上面的代碼可以看到,無論載main函數里面生成什么對象Circle、triangle等,只要交給void f(Shape s)函數去辦就可以了,它知道應該調用哪一段代碼,即產生正確的行為,多代使得我們代碼量減少而且容易擴展。
如果在構造器調用多態的函數,那么就容易產生一些問題,如下所示:
class Glyph{
?void draw(){System.out.println("Glyph.draw()");};
?Glyph(){
??System.out.println("Glyph() before draw()");
??draw();
??System.out.println("Glyph() after draw()");
?}
?
}
class RoundGlyph extends Glyph{
?private int radius=1;
?RoundGlyph(int r){
??radius=r;
??System.out.println("RoundGlyph.RoundGlyph(),radius="+radius);
?}
?void draw(){
???System.out.println("RoundGlyph.draw(),radius="+radius);
?}
}
public class PolyConstructors{
?public static void main(String[] args){
???new RoundGlyph(5);
?}
}
運行結果為:
Glyph() before draw()
RoundGlyph.draw(),radius=0
Glyph() after draw()
RoundGlyph.RoundGlyph(),radius=5
因為?new RoundGlyph(5)會先調用基類的構造器,所以第一句結果沒問題,而基類的構造起調用了draw()方法,這個時候產生了多態,即實際調用繼承類的draw()方法,但是為什么打印的是radius=0呢?為什么不是radius=5或編譯通不過,radius沒有定義?編譯確實沒有出問題,原來初始化最開始一步并不是調用基類的構造函數,而是將分配給對象的存儲空間初始化為零,也就是說radius在調用基類構造函數之前就已經初始化了,只是初始化值為0。所以輸出結果為radius=0。這個輸出結果并不是我們所希望的,所以編寫構造起時記住一個原則:盡可能簡單使對象進入正常狀態,如果可以的話,避免調用其他方法。當然調用基類的final方法和private方法是安全的。