1. 程式人生 > >Java多型——向上轉型與向下轉型

Java多型——向上轉型與向下轉型

Java的轉型之前一直處於似是而非的狀態。

其中尤其是向上轉型,大概意思誰不懂啊,A  a  =  new  B()  嘛,然而往細了摳就有點矇蔽,尤其是面對考卷裡出現的那種賊JB噁心的多型題,簡直要哭了。

翻閱了幾篇大佬們寫的部落格,整理一下思路總結如下。

首先先從最基本的解起:

向上轉型即A a = new B()  也就是父類的引用指向了子類的物件

A a;//建立父類A物件的引用a
a = new B()//new了一個子類物件B被父類的引用a所指向

這也就是向上轉型,此時a指向了一個子類的物件

由於父類引用a指向了子類的物件B,因此可以有B b = (B)a,因為a已經指向了子類所以可以強轉成B

但如果引用a指向的是正常的父類物件A,也就是 A a = new A();那麼對於B b = (B) a;會執行出錯;

這是因為對於第一個例子而言子類的引用當然可以指向子類的物件,而對於第二個例子而言子類的物件妄圖去指向父類的物件,越俎代庖?

有了最基本的轉型的概念,下面舉一個向上轉型的小例子:

public class Animal {  
      
    public void eat(){  
        System.out.println("animal eatting...");  
    }  
}  
class Bird extends Animal{  
      
    public void eat(){  
        System.out.println("bird eatting...");  
    }  
      
    public void fly(){  
          
        System.out.println("bird flying...");  
    }  
}  
class Main{  
      
    public static void main(String[] args) {  
          
        Animal b=new Bird(); //向上轉型  
        b.eat();   
        //! error: b.fly(); b雖指向子類物件,但此時丟失fly()方法  
    } 
}
結果是bird eatting...
這裡子類Birl繼承了父類Animal方法同時重寫了父類的方法eat(),main方法裡父類的引用b指向了子類bird物件因此呼叫的eat方法是子類的eat方法

而fly方法是子類特有的方法,對應變數b而言由於是父類的引用並不認識子類特有的方法,所以b.fly會報錯

簡而言之 b實際指向的是子類,因此會呼叫子類本身的方法。同時向上轉型時引用變數b會遺失除與父類物件共有的其他方法,因此fly()不為b所識別。

上面舉個向上轉型的小例子幫助瞭解,那麼在實際專案中有什麼用處呢?

個人感覺提高了程式碼的複用性,舉個小例子;

public class Human {  
    public void sleep() {  
        System.out.println("Human sleep..");  
    }  
}  
class Male extends Human {  
    @Override  
    public void sleep() {  
        System.out.println("Male sleep..");  
    }  
}  
class Female extends Human {  
    @Override  
    public void sleep() {  
        System.out.println("Female sleep..");  
    }  
} 
class Main{  
      
    public static void main(String[] args) {  
          
        dosleep(new Male());  //傳入的引數是子類
        dosleep(new Female());  
    }  
  
    public static void dosleep(Human h) {   //方法裡的引數是父類
        h.sleep();  
    }  
}  
結果:Male sleep…
Female sleep…

為什麼說傳入的引數是子類的物件而方法裡的引數是父類引用呢,是因為只有這樣才是向上轉型所對應的

Human h = new Male(); Human h = new Female();

可以看到,由於使用的向上轉型,那麼Main類裡只需要一個dosleep方法即可,程式會根據傳入的引數自動去呼叫對應的子類方法。

一般在大型專案開發中,往往是多個類實現了同一個介面或繼承同一個父類,如果說不使用向上轉型,那麼也就是子類每有一個父類方法在父類中都需要書寫一個方法

這樣會造成整個專案的冗雜,而採用轉型的概念後,父類或者介面中只需要有一個方法即可,這樣會使上層邏輯抽象,獨立清晰方便以後擴充套件。

結尾在分析一下部落格裡出現很多的噁心例子來鞏固一下

public class A {  
    public String show(D obj) {  
        return ("A and D");  
    }  
  
    public String show(A obj) {  
        return ("A and A");  
    }   
  
}  
  
public class B extends A{  
    public String show(B obj){  
        return ("B and B");  
    }  
      
    public String show(A obj){  
        return ("B and A");  
    }   
}  
  
public class C extends B{  
  
}  
  
public class D extends B{  
  
}  
  
public class Test {  
    public static void main(String[] args) {  
        A a1 = new A();  
        A a2 = new B();  
        B b = new B();  
        C c = new C();  
        D d = new D();  
          
        System.out.println("1--" + a1.show(b));  
        System.out.println("2--" + a1.show(c));  
        System.out.println("3--" + a1.show(d));  
        System.out.println("4--" + a2.show(b));  
        System.out.println("5--" + a2.show(c));  
        System.out.println("6--" + a2.show(d));  
        System.out.println("7--" + b.show(b));  
        System.out.println("8--" + b.show(c));  
        System.out.println("9--" + b.show(d));        
    }  
}  
結果
1--A and A  
2--A and A  
3--A and D  
4--B and A  
5--B and A  
6--A and D  
7--B and B  
8--B and B  
9--A and D  


這個噁心的例子應該不止一次看到,我也來摻一腳給一下我自己的解法

對於前三個而言均是a1的方法呼叫,變數a1是直接指向物件A的,即最常見的A a = new A()的情況,那麼可能的答案就只有A and A 和 A and D兩種

對於前兩個引數分別是b和c,在A的兩個方法裡沒有直接的指向,因此想到C繼承B繼承A,所以輸出結果既是A and A

對於第三個引數是d,在A中有明確的方法,因此輸出A and D (這個應該是最不會出錯的)

對於4~6而言均是a2的方法呼叫,變數a2是父類的引用指向子類物件B,參考前文在轉型的過程中會遺失方法show(B obj),又由於方法show(A obj)繼承自父類A

因此可能的答案只有B and A 和 A and D兩種

所以對於第四個和第五個的引數b和c,由於方法show(B obj)遺失了,因此只能找到最近的show(A obj),又由於這個方法被子類所重寫,因此輸出的是子類方法中的B and A而非父類的A and A

對於第六個引數是d ,在父類中有該方法,因此輸出A and D

對於7~9而言均是b的方法呼叫,變數b指向的是子類B的物件,而子類B繼承了父類A,因此可能的答案為B and B ,B and A ,A and D,父類的A and A被重寫了。

因此7~9的輸出也不再有什麼問題了。


參考部落格:http://blog.csdn.net/thinkGhoster/article/details/2307001

    http://blog.csdn.net/mr_jj_lian/article/details/6860845