学習の記録−9

F#でのOOP(クラスの書き方)について

プロパティの定義

まずはプロパティの書き方。慣れないと接頭辞のprt.*1が結構気持ち悪い感じ。以下のコードでは性別を判別共有体として作っておいて、それをメンバ変数(sex,F#ではそう呼ばない?)として持たせておく。んで、その変数に対してセッター・ゲッターを定義している。

type Sex = Man | Woman
type Human(sex_ : Sex) =
    let mutable sex = sex_
    member prt.Sex 
        with get() = sex
        and  set(x) = sex <- x
;;

実行してみると以下のような感じになる。

> let x = new Human(Man);;

val x : Human

> x.Sex;;
val it : Sex = Man
> x.Sex <- Woman;;
val it : unit = ()
> x.Sex;;
val it : Sex = Woman

メソッドの定義

このクラスに性別を日本語の文字列で返すメソッド(ShowSex)*2を追加定義してみる。

type Sex = Man | Woman
type Human(sex_ : Sex) =
    let mutable sex = sex_
    member prt.Sex 
        with get() = sex
        and  set(x) = sex <- x
    member prt.ShowSex() = 
        match sex with
            | Man -> "男性"
            | Woman -> "女性"
                
;;

実行すると

> let x = new Human(Woman);;

val x : Human

> x.ShowSex();;
val it : string = "女性"

となる。

コンストラクタの書き方

コンストラクタも複数定義できて*3、以下のようにnewを使って書く。このとき、プライマリーコンストラクターを引数多くしておいた方が使い勝手がいい。ここではname属性も併せてクラスに追加している。

type Sex = Man | Woman
type Human(sex_ : Sex, name_ : string) =
    let mutable sex = sex_
    let mutable name = name_
    new(sex_) = 
        match sex_ with
            | Man -> Human(sex_, "太郎")
            | Woman -> Human(sex_, "花子")
    member prt.Name
        with get() = name
    member prt.Sex 
        with get() = sex
        and  set(x) = sex <- x
    member prt.ShowSex() = 
        match sex with
            | Man -> "男性"
            | Woman -> "女性"
                
;;

実行結果は

> let x = new Human(Man);;

val x : Human

> x.Name;;
val it : string = "太郎"
> let x = new Human(Woman, "良子");;

val x : Human

> x.Name;;
val it : string = "良子"

となる。

staticなメンバーの書き方

staticなメンバーも作ることが可能。

type StaticClass = 
    static member Add(x, y) = x + y
;;
> StaticClass.Add(3,5);;
val it : int = 8

デフォルト引数の書き方

メソッドに対してデフォルト引数を?変数名として定義することもできる。値が渡されなかった時はNoneが入っている。

type StaticClass = 
    static member Add(x, y, ?z) = 
        let z = defaultArg z 0
        x + y + z
;;
> StaticClass.Add(1,1);;
val it : int = 2
> StaticClass.Add(1,1,10);;
val it : int = 12

クラスの継承

抽象クラスを作成してそれを継承するようなOOPっぽいコードは以下のように書くらしい。ここでは動物(Animal)クラスを継承して人間クラスを作成してみる。

[<AbstractClass>]
type Animal() =
    abstract member Shout : unit -> string
type Human() = 
    inherit Animal()
    override x.Shout() = 
        "俺は人間だーっ!!!"
;;

実行結果は

> let y = new Human();;

val y : Human

> y.Shout();;
val it : string = "俺は人間だーっ!!!"

となる。ここでx.Shoutのx.というのを入れないと

error FS0673: This instance member needs a parameter to represent the object being invoked. Make the member static or use the notation 'member x.Member(args) = ...'.

みたいなエラーが出て怒られた。あとクラスの宣言・定義時の()もないとだめ。コンストラクタがないとか言って怒られる。逆に上のStaticClassだとOKなのをみるとオブジェクトを生成する時はMUSTっぽい。

インターフェイスの記述

使用する際はいちいちアップキャストしないとだめっぽい。

type ISpeak = 
    abstract Speak : unit -> string
type Human(name_) =
    let name = name_
    interface ISpeak with
        member this.Speak() = "My Name is " + name      
    member this.Name() = 
        let x  = this :> ISpeak
        x.Speak()
;;

実行結果

> let x = new Human("太郎");;

val x : Human

> x.Name();;
val it : string = "My Name is 太郎"

*1:何でもいいっぽい

*2:F#では引数を持つメンバーをメソッドと呼ぶらしい

*3:デフォルトコンストラクタ(F#ではプライマリーコンストラクターはクラスの宣言時に作っちゃってるイメージ