F#入門-第四章 面向對象-第五節 繼承

應該使用繼承的場合
    繼承是指以現有的類(父類)爲基礎,定義新類(子類)。子類繼承父類的所有功能,可以對父類的功能進行改寫,可以追加新的功能。繼承的功用是十分強大的,但是如果使用不得當的話,會使程序設計變得非常奇怪,所以首先讓我們來看應該使用繼承的正確場合。

    通過繼承,子類繼承了父類的全部功能,也就是說,子類可以說是父類的一種,這裏是重點,一般說來只有當“子類就是父類的一種”這種關係成立的時候,才應該使用繼承。

    拿汽車來舉例說明,“公共汽車是汽車的一種“,“小轎車是汽車的一種“,這種說法是正確的,所以公共汽車類或小轎車類繼承汽車類是不會有什麼問題的。但是,“輪胎是汽車的一種“這種說法是錯誤的,所以輪胎類繼承汽車類是不正確的。

    這種“A是B的一種”的關係在面向對象的專門術語種叫做“is-a關係”,談到繼承的原則時,只有“is-a關係”成立時才應該進行繼承。同時,“A具有B"這種關係叫做"has-a關係"。上例中,因爲可以說“汽車具有輪胎”,所以汽車和輪胎的關係是"has-a關係",當這種"has-a關係"成立時不應該使用繼承,而應該使用聚合或複合的技術,這樣才能很好地進行程序設計。關於聚合與複合,在後文詳述。

繼承
  F#的繼承與Java和C#語言中的繼承一樣,只支持單父類(與接口)繼承。這恐怕是因爲多父類繼承(多重繼承)會產生譬如菱形繼承等各種各樣問題的緣故。

    類的繼承的書寫方法爲在class關鍵字後加inherit關鍵字,然後指定父類。

    首先,創建父類。

準備:父類的定義
type Mouse = class
    val name : string
    new (n) = {name = n}
    end;;
let hatuka = new Mouse("Hatuka nezumi");;


    上例中創建了老鼠類。就象例子中記述的那樣,該類只具有名字。接着,我們創建一個繼承該類並具有口袋的老鼠類。

類的繼承
type PocketMouse = class inherit Mouse
    val pocket : string
    new (namae,contents) = {inherit Mouse(namae);pocket = contents;}
    end;;
let dora = new PocketMouse("doraemon","4jigen pocket");;
let pika = new PocketMouse("pikachu","denki bukuro");;


    上例中創建了繼承mouse類的pocketmouse類。pocketmouse類繼承了mouse類的全部成員,所以也具有沒有特別聲明的name字段。

    class inherit mouse的部分爲實現繼承的部分,利用該表達式聲明mouse類的繼承。同時,在構造器的初始化的位置,調用父類的構造器,調用時使用inherit關鍵字。

    創建類時,知道父類與子類的構造器的調用順序是十分有用的。接下來的例子中,確認構造器的調用順序。

構造器的調用順序
type Mouse = class
    val name : string
    new (n) = {name = n} then System.Console.WriteLine("base class constructor called")
    end;;
type PocketMouse = class inherit Mouse
    val pocket : string
    new (namae,contents) = {inherit Mouse(namae);pocket = contents;}
    then System.Console.WriteLine("sub class constructor called");
    end;;
let dora = new PocketMouse("doraemon","4jigen pocket");;


    正如下面執行結果所示,首先調用基本類的構造器。

執行結果
base class constructor called
sub class constructor called
按任何鍵繼續 . . .


    另外,在子類的構造器的初始化的部分,不想顯式調用父類的構造器的時候,可以使用()作爲參數來調用構造器。

簡化了初始化的構造器
type Mouse = class
    val name : string
    new () = {name = "nezumi"} then System.Console.WriteLine("base class constructor called")
    end;;
type PocketMouse = class inherit Mouse
    val pocket : string
    new (contents) = {pocket = contents;}
    then System.Console.WriteLine("sub class constructor called");
    end;;
let dora = new PocketMouse("4jigen pocket");;


    其執行結果與前面的執行結果相同。

執行結果
base class constructor called
sub class constructor called
按任何鍵繼續 . . .


    正如下例所示,當沒有接受()作爲參數的構造器時,會產生編譯錯誤。

沒有接受()作爲參數的構造器時會產生編譯錯誤
type Mouse = class
    val name : string
    new (n) = {name = n} then System.Console.WriteLine("base class constructor called") 
    end;;
type PocketMouse = class inherit Mouse
    val pocket : string
    new (contents) = {pocket = contents;}
    then System.Console.WriteLine("sub class constructor called");
    end;;
let dora = new PocketMouse("4jigen pocket");;


    最後,使用默認的構造器,書寫的程序如下所示。

默認的構造器
type Mouse(n) = class
    member x.name = n : string
    end;;
type PocketMouse(n,contents) = class inherit Mouse(n)
    member x.pocket = contents : string
    end;;
let dora = new PocketMouse("doraemon","4jigen pocket");;


    這種情況下,調用父類的構造器時的參數是在用inherit關鍵字聲明父類時一起指定的。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章