1. 程式人生 > >面向物件——多型與向上、向下轉型機制

面向物件——多型與向上、向下轉型機制

前言

開頭先回憶一下,面向物件的三大特徵:封裝(資料抽象)、繼承、多型。為什麼多型排在最後一位,因為它是以前面兩個為前提的,尤其是繼承。

多型概念梳理

多型本質在於 同一種行為的多樣化表達 (這句話不禁讓我想起了基因的多樣性表達,這是現實世界物種多型的原因)。

對於某一種行為而言,它的多樣性體現在兩種可能:
1. 行為主體相同,行為受體不同——同樣是待客,為什麼他對別人溫柔對你凶?
2. 行為受體相同,行為主體不同——同樣是吃肉,人是煮熟吃,動物生吃。

這是我們在生活中的例子,現在我們遷移到程式設計中的多型!

我們是不是可以把一個方法稱為一種行為,他的呼叫者我們稱為“行為主體” ,他的引數我們稱為”行為受體”呢,而把函式體看作是行為的具體內容?

程式設計中,多型分為 編譯時多型 和 執行時多型,也稱為 前期繫結動態繫結

注:存在說法認為多型僅僅限定於執行時的多型,本人認為還是以“同一種行為的多樣化表達”的基準,說法是死的,思想是活的,大家也不用在這方面糾結。

編譯時多型:程式設計中的體現是方法過載,呼叫者是明確的,呼叫方法有多種,我們根據引數來確定呼叫哪個方法,從而體現多樣性。
執行時多型:根源於繼承之後的方法重寫,多個子類都繼承了相同父類,並且都對父類中某個普通方法進行了重寫,從而體現多樣性。

實驗

首先,先建立繼承關係如下:
image_1clkqo76a8jg14i41g8u17cfkj4d.png-12.3kB
image_1clkqmn0m4q01mr8j91urq1cc230.png-11.5kB
image_1clkqklrcrni1ov928h1ig2mqq2j.png-12.3kB

轉型

能實現轉型,是執行時多型的必要條件之一。
編譯的機制:


根據你引用指定的型別去搜索你的方法是否存在,如果該引用型別中不存在呼叫方法,報錯。
執行時機制
執行時,是根據引用所指的具體物件型別來呼叫方法。
當我們將父類中的study()方法註釋:
image_1clkqhovg3n9oh41sihtsf6t1p.png-12.5kB
執行時:
image_1clkqjqgvkmn19uucks159mdce26.png-17.9kB
從機器級角度分析這個問題:
編譯時根據物件引用確定該方法的符號引用名稱,執行時根據物件引用指向的物件進行符號引用的重定位。

向上轉型

如果你是一個大學生,那麼你肯定也是一名學生。
我們對你所屬的範圍進行了一次周延擴大,這就是一次向上轉型。

用處:
1.提高了程式的擴充性,不需要寫一些重複的程式碼。
2.增加程式碼簡潔性,可閱讀性。

假設我們有一個老師,他既教高中生,也教大學生,不同學生上課方式不同,正常那我們不是要進行方法過載為每個學生量身定製一個方法嗎?但是我們可以讓他們自適應:
image_1clkqu5hefucqoh1e6rrce6eu5d.png-7.8kB


image_1clkrf32l12p71ige1j2b1i1tdmo8h.png-20kB
結果:
image_1clkrffjc1oje1p5m13nctm2ele8u.png-10.7kB

向上轉型進階分析:
如果方法呼叫者和方法引數都是具有繼承關係的型別,這個向上轉型是怎麼轉的呢?
觀查測試樣例:

image_1cll2h6gt4ai51q14dg1fpfrlka8.png-25.3kB

image_1cll2enkf14eg1ulvpktijhh9n9r.png-13.9kB

輸出:
    A and B

當我們將A類中的show(B)註釋掉:
image_1cll2isddv8jiig1s68106ed9gal.png-24.1kB
image_1cll2kq401lqu14di10dsjmtt56b9.png-13.5kB

輸出:
    B and A

規律總結:

先是呼叫者由下到上向上轉型查詢對應方法; 若沒找到,每次將呼叫引數向上轉型一次,再尋找對應方法。

向下轉型(注意點)

你是一個學生,但不一定是大學生。向下轉型,一定要注意型別的匹配。
為什麼又會有向下轉型?向上轉型的弊端在於被呼叫方法受到侷限,而當你又想呼叫子類獨有的方法,你就必須用 向下轉型 轉回去。
所以向下轉型一定是先有向上轉型作鋪墊的。
但是,這邊注意一點,你物件是什麼型別,你轉回去也應該是什麼型別。

人->動物->豬 ×
人->動物->人 √
image_1clkr5u6k18rvntl1im01vq11sh26q.png-26.7kB
編譯沒有報錯,但執行一定丟擲轉型異常。
image_1clkramai1mmrr34rcs1nmpuui77.png-10kB
正確做法:
image_1clkrcdf327cvvg1og11j0d1g1h7k.png-13kB

牛刀小試

根據我們之前總結的規律,看看你的答案是否正確吧:

class A {
    public String show(D obj) {
        return ("A and D");
    }

    public String show(A obj) {
        return ("A and A");
    }

}

class B extends A{
    public String show(B obj){
        return ("B and B");
    }

    public String show(A obj){
        return ("B and A");
    }
}

class C extends B{

}

class D extends B{

}

public class Demo {
    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