かずきのBlog@hatena

すきな言語は C# + XAML の組み合わせ。Azure Functions も好き。最近は Go 言語勉強中。日本マイクロソフトで働いていますが、ここに書いていることは個人的なメモなので会社の公式見解ではありません。

手軽なスクリプト言語としてのF# その10「継承・アブストラクトクラス」

さて、記念すべき10回目のエントリです。やっと継承できるようになりました。因みに、この一連のエントリのタイトルでスクリプト言語として〜と書いてますが、このエントリのサンプルを作るときはばっちりコンパイルしてるので若干矛盾してなくもない今日この頃です。まぁ気にせずいってみます。

アブストラクトなクラス

クラスを継承する際のベースクラスは別にアブストラクトなクラスじゃなくてもいいのですが、ついでなのでアブストラクトなクラスにしてみようと思います。アブストラクトなクラスの定義は以下のようになります。

// アブストラクトなクラスの定義
[<AbstractClass>]
type Animal(name) =
    let name = name

    member this.Name with get() = name

    // 引数を受け取らないで何も戻り値がない仮想メソッドCryの定義
    abstract Cry : unit -> unit

    // Cryのデフォルト実装を提供出来る
    default this.Cry() =
        printfn "%s > ...." this.Name

ちょっと変わってるのがAbstractClass属性を指定してやることでアブストラクトなクラスになるという点です。アブストラクトなメンバーは、memberの代わりにabstractを使って定義します。上の例では引数無し、戻り値無しのCryというメソッドが、それにあたります。因みにmemberのかわりにabstractを使うというのは、ちょっと嘘で本当はabstract memberと書くところをmemberを省略できるというだけです。実用上はmemberの代わりにabstractを使うと覚えてても差し支えは無いと思います。

もちろんabstract Hoge : string with getこのような形でアブストラクトなプロパティも定義出来ます。(ここでは使ってません)

継承の仕方

継承は、inheritキーワードを使用して記述します。大体以下のようになります。

type クラス名(引数) =
  inherit 基本クラス名(引数)
  
  override this.プロパティ名 = 値

  override this.メソッド名(引数) =
    メソッドの中身

上記の例ではoverrideの仕方しか書いてませんが、ふつうにメソッドやプロパティを派生クラスで定義することも出来ます。とりあえず、Animalクラスを継承してDogクラスを定義してみました。犬はワンワンと鳴きます。

// 独自の鳴き声を持つクラス1
type Dog(name) =
    inherit Animal(name)

    override this.Cry() =
        printfn "%s > ワンワン" this.Name

ポリモフィズムしてみよう

ということで、オブジェクト指向の例題とかで使われるけど、オブジェクト指向のメリットは全然理解できずにポリモフィズムの挙動はばっちり理解できる動物と犬や猫といった例を使ったプログラムと実行結果を示して、この回を締めくくろうと思います。

// アブストラクトなクラスの定義
[<AbstractClass>]
type Animal(name) =
    let name = name

    member this.Name with get() = name

    // 引数を受け取らないで何も戻り値がない仮想メソッドCryの定義
    abstract Cry : unit -> unit


    // Cryのデフォルト実装を提供出来る
    default this.Cry() =
        printfn "%s > ...." this.Name

// アニマルのデフォルト実装を使うクラス
type Ferret(name) =
    inherit Animal(name)

// 独自の鳴き声を持つクラス1
type Dog(name) =
    inherit Animal(name)

    override this.Cry() =
        printfn "%s > ワンワン" this.Name

// 独自の鳴き声を持つクラス2
type Shibayan() =
    inherit Animal("shibayan")

    override this.Cry() =
        printfn "%s > メイド ニーソ" this.Name

// 独自の鳴き声を持つクラス3
type 割と普通() =
    inherit Animal("割と普通")

    override this.Cry() =
        printfn "%s > エロース エロース(深夜の鳴き声" this.Name

// 動物のリストを定義
let animals : Animal list = [ 
    Dog("ポチ"); 
    Shibayan(); 
    Ferret("ふぇれっと"); 
    割と普通() ]

// 皆に鳴いて貰う
for animal in animals do
    animal.Cry()

実行すると以下のようになります。

ポチ > ワンワン
shibayan > メイド ニーソ
ふぇれっと > ....
割と普通 > エロース エロース(深夜の鳴き声

ちゃんとベースクラスの型で受けて、メソッドの呼び出し結果は派生クラスになるというポリモフィズムの動きが確認できると思います。今日は以上です。