我們先來看stackoverflow上的一個問題,先上代碼
1 public class Piece{
2 public static void main (String [] args){
3 Piece p2 = new Knight();
4 Knight p1 = new Knight();
5 p1.capture(p2);
6 p2.capture(p1);
7 }
8 public void capture(){
9 System.out.println("Capture");
10 }
11 public void capture(Piece p){
12 System.out.println("I'm bored");
13 }
14 }
15 class Knight extends Piece{
16 public void capture(Piece p){
17 System.out.println("Knight is bored");
18 }
19 public void capture(Knight k){
20 System.out.println("Too slow buddy");
21 }
22 }
問的是這段代碼輸出是什麼?答案是
Knight is bored Knight is bored
因為剛剛開始學習Java,在跟CS61B的課,然後老師講到dynamic type和static type的時候,自己有點模糊,於是在stackoverflow上搜索相關的問題,果然發現一個典型的動態綁定的問題,仔細分析之後感覺收獲挺大的,對繼承的理解更加深入了。
分析:這個是怎麼回事呢?首先看第三第四行,p1肯定是一個Knight類型,而對於p2, 它是一個Piece類型的引用,指向了一個Knight類的對象,此時我們稱Piece為static type,而Knight為dynamic類型。
p1.capture(p2),也就是Knight.capture(Piece),Knight類裡剛好定義了這個方法,即輸出"Knight is bored"。
p2.capture(p1),也就是Piece.capture(Knight),乍一看Piece裡面沒有這個方法啊。這裡應該是有一個賦值,即Piece p = p1,把子類的引用賦給父類,這是允許的。所以實際上調用的Piece.capture(Piece),那麼結果應該輸出"I'm bored"啊,怎麼會是"Knight is bored"呢?前面已經說了,p2實際上指向了一個Knight類的對象,而在Knight類中剛好Ovriride了這個Piece.capture(Piece)方法,也就是Knight.capture(Piece),所以應該調用這個方法,即輸出"Knight is bored"。
好現在我們把這個Override的方法注釋掉,看看結果如何?
1 public class Piece{
2 public static void main (String [] args){
3 Piece p2 = new Knight(); //static Piece, dynamic Knight
4 Knight p1 = new Knight();
5 p1.capture(p2);
6 p2.capture(p1);
7 }
8 public void capture(){
9 System.out.println("Capture");
10 }
11 public void capture(Piece p){
12 System.out.println("I'm bored");
13 }
14 }
15 class Knight extends Piece{
16 // public void capture(Piece p){ //這裡override了Piece中的方法
17 // System.out.println("Knight is bored");
18 // }
19 public void capture(Knight k){
20 System.out.println("Too slow buddy");
21 }
22 }
輸出結果是:
I'm bored I'm bored
分析:第二個輸出剛剛已經解釋了,因為現在沒有override的方法了,所以就直接調用Piece類中的Piece.capture(Piece p)方法。那麼第一個輸出又是怎麼回事呢?p1.capture(p2),也就是Knight.capture(Piece),這裡不能調用Knight.capture(Knight k)這個方法,因為Knight k = p2,這是不合法的,Java中不允許把父類的引用賦給子類的引用變量。那麼既然如此,Knight類中豈不是沒有這個方法了嗎?不要忘記,Knight類是繼承在Piece類上的,也就是Knight類型也能調用Piece類中的方法,也就是Piece.capture(Piece)方法,所以輸出是"I'm bored"。
現在我們知道,Piece p2 = new Knight()這句特殊之處就在於,有一個查找在子類中是否存在override的方法,如果存在就調用子類中這個override的方法,如果沒有就使用父類中的方法。如果是這樣定義的:Piece p2 = new Piece(),那麼無論在子類中有沒有override方法,都不會去調用子類的方法,而是直接調用父類的方法。如下:
1 public class Piece{
2 public static void main (String [] args){
3 // Piece p2 = new Knight(); //static Piece, dynamic Knight
4 Piece p2 = new Piece();
5 Knight p1 = new Knight();
6 p1.capture(p2);
7 p2.capture(p1);
8 }
9 public void capture(){
10 System.out.println("Capture");
11 }
12 public void capture(Piece p){
13 System.out.println("I'm bored");
14 }
15 public void capture(Knight p){
16 System.out.println("Hello");
17 }
18 }
19 class Knight extends Piece{
20 public void capture(Piece p){ //這裡override了Piece中的方法
21 System.out.println("Knight is bored");
22 }
23 public void capture(Knight k){ //這裡override了Piece中的方法
24 System.out.println("Too slow buddy");
25 }
26 }
輸出結果是:
Knight is bored Hello
我們可以看到,即使在Knight類中override了.capture(Knight)方法,由於p2一直就是一個Piece類,根本不會調用Knight類中的方法,所以p2.capture(p1),也就是Piece.capture(Knight),輸出"Hello"。第四行改成Piece p2 = new Knight(),則輸出就是:
Knight is bored Too slow buddy
第二個輸出調用的就是Knight類中override的方法。