学習の記録−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 太郎"