抽象メソッド
抽象メソッドとは、実装を持たないメソッドのことです。
実装がないメソッドが何の役に立つんだ?と思われますが
実際に使用する際には
1.デフォルトの実装を定義するか
2.継承して(実装を提供することによって)利用
します。
抽象メソッドを利用するにはabstractキーワードを用います。構文についてはメソッドのページを参考にしてください。
また、実装がないため、通常のメンバの宣言のように
自分自身への参照を持たせる必要はありません。
かわりに、メソッドの型名を記述します。
また、デフォルトの実装を提供するために
defaultキーワードを用います。
抽象メソッドを持つクラス
type Animal() = class
abstract Bark : unit -> unit
default x.Bark ()=printfn "wan wan"
end;;
let a = new Animal() in a.Bark();;
[<AbstractClass>]
type AbstractAnimal() = class
abstract Bark : unit -> unit
end;;
// let b = new AbstractAnimal() in b.Bark();; // エラー。抽象クラスはインスタンス化できない
抽象メソッドを持つクラス(軽量構文)
type Animal() =
abstract Bark : unit -> unit
default x.Bark ()=printfn "wan wan"
let a = new Animal() in a.Bark();;
[<AbstractClass>]
type AbstractAnimal() =
abstract Bark : unit -> unit
// let b = new AbstractAnimal() in b.Bark();; // エラー。抽象クラスはインスタンス化できない
ありがちな例ですが、
動物は鳴く、ということを抽象化したのが
このAnimalクラスです。
Barkが抽象メソッドになっています。
そして、defaultキーワードを用いて
デフォルトの実装を提供しています。
デフォルトの実装を持つ場合はインスタンス化できますが
デフォルトの実装がない場合にはインスタンス化できません。
デフォルトの実装を持たない抽象メソッドを持つクラスは抽象クラスと呼ばれ、
[<AbstractClass>]をクラスの宣言の前につける必要があります。
抽象クラスは継承して使用することを前提としたクラスになります。
オーバーライド
クラスの継承で、子クラス側で親クラスの機能の一部を
上書きできると説明しましたが
abstractメソッドをつけて宣言したメンバは
子クラス側で上書きすることが出来ます。
これをメソッドのオーバーライドと言います。
(memberで宣言したものはオーバーライドできません)
オーバーライドは、オブジェクト指向での
「多態性(Polymorphism)」を実現するための一機能になります。
メソッドをオーバーライドするには
overrideキーワードを用います。
なお、内部的にはoverrideキーワードとdefaultキーワードはまったく同じものです。
参考
しかし、デフォルトの実装の提供にoverrideを使ったりオーバーライドするメソッドにdefaultをつけたりすることは
混乱を招くのでやめた方が良いと思います。
先のAnimal/AbstractAnimalクラスを継承して
DogクラスとCatクラスを作ってみます。
メソッドのオーバーライド
type Animal() = class
abstract Bark : unit -> unit
default x.Bark ()=printfn "wan wan"
end;;
let a = new Animal() in a.Bark();;
[<AbstractClass>]
type AbstractAnimal() = class
abstract Bark : unit -> unit
end;;
type Dog() = class
inherit Animal()
override x.Bark() = printfn "わんわん"
end;;
type Cat() = class
inherit AbstractAnimal()
override x.Bark() = printfn "ニャー"
end;;
let b = new Dog() in b.Bark();;
let c = new Cat() in c.Bark();;
メソッドのオーバーライド(軽量構文)
type Animal() =
abstract Bark : unit -> unit
default x.Bark ()=printfn "wan wan"
let a = new Animal()
a.Bark()
[<AbstractClass>]
type AbstractAnimal() =
abstract Bark : unit -> unit
type Dog() =
inherit Animal()
override x.Bark() = printfn "わんわん"
type Cat() =
inherit AbstractAnimal()
override x.Bark() = printfn "ニャー"
let b = new Dog()
b.Bark()
let c = new Cat()
c.Bark()
Dog/Catクラスでは鳴き声を変えるため、
Barkメソッドをオーバーライドしています。
Catクラスは抽象クラスを継承していますが、
抽象メソッドをもつAnimalクラスとほとんど使い方に差が無いことがわかります。
Animalクラスはデフォルトでwan wanと鳴くので
このケースでは一般の動物がすべてデフォルトでwan wanと鳴くことになってしまい
設計としてはAbstractAnimalのほうが優れていると言えます。
この例のように抽象メソッドだけを持つクラスを宣言するのであれば
次に説明するインタフェースを用いたほうが良いと思います。
AbstractClassを用いるのは
ある程度は実装を提供するが
一部のメソッドだけ抽象メソッドにしたい
というようなケースになるでしょう。