抽象クラス (F#)
抽象クラスは、一部またはすべてのメンバーを実装しないようにして、派生クラスから実装できるようにするためのクラスです。
// Abstract class syntax.
[<AbstractClass>]
type [ accessibility-modifier ] abstract-class-name =
[ inherit base-class-or-interface-name ]
[ abstract-member-declarations-and-member-definitions ]
// Abstract member syntax.
abstract member member-name : type-signature
解説
オブジェクト指向プログラミングでは、抽象クラスは階層構造の基本クラスとして使用され、多種多様なオブジェクトに共通の機能を表します。"抽象" という名前が意味するように、抽象クラスは、問題ドメインの具体的なエンティティに直接対応していない場合があります。ただし、抽象クラスは、各種の具体的なエンティティに共通するものを表しています。
抽象クラスには AbstractClass 属性が必要です。抽象クラスは、実装されたメンバーと実装されていないメンバーを持つことができます。抽象という用語をクラスに対して使用するときは、他の .NET 言語の場合と同じ意味で使用しますが、F# で、メソッド (およびプロパティ) に対して抽象という用語を使用するときは、他の .NET 言語の場合とは若干異なる意味で使用します。F# では、メソッドが abstract キーワードでマークされていると、メンバーがその型の仮想関数の内部テーブルにエントリ (仮想ディスパッチ スロットとも呼ばれます) を持つことを示します。つまり、virtual キーワードが F# 言語で使用されていなくても、そのメソッドは仮想メソッドになります。abstract キーワードは、メソッドが実装されているかどうかに関係なく、仮想メソッドで使用されます。仮想ディスパッチ スロットの宣言は、そのディスパッチ スロットのメソッドの定義とは別のものです。したがって、F# では、抽象メソッドの宣言と個別の定義の両方と、default キーワードまたは override キーワードとの組み合わせが、他の .NET 言語の仮想メソッドの宣言と定義に相当します。使用例を含む詳細については、「メソッド (F#)」を参照してください。
クラスが抽象クラスと見なされるのは、宣言されているが、定義されてはいない抽象メソッドがある場合のみです。したがって、抽象メソッドのあるクラスが必ず抽象クラスであるとは限りません。クラスに未定義の抽象メソッドがある場合を除いて、AbstractClass 属性は使用しないでください。
前の構文の accessibility-modifier は、public、private、または internal です。詳細については、「アクセス制御 (F#)」を参照してください。
他の型と同様に、抽象クラスは基本クラスと 1 つ以上の基本インターフェイスを持つことができます。各基本クラスまたは基本インターフェイスは、inherit キーワードと共に、個別の行に記述します。
抽象クラスの型定義には、完全に定義されたメンバーを含めることができますが、抽象メンバーを含めることもできます。抽象メンバーの構文は、前の構文で別々に示しています。この構文では、メンバーの type signature は、パラメーターの型を順番に並べたリストです。このリストには、カリー化したパラメーターおよび組のパラメーターに応じて -> トークンや * トークンで区切られる戻り値の型も含まれます。抽象メンバーの型シグネチャの構文は、シグネチャ ファイルで使用される構文や Visual Studio コード エディターの IntelliSense で示される構文と同じです。
使用例
次のコードは、2 つの非抽象派生クラス、Square と Circle を持つ Shape 抽象クラスを示しています。この例では、抽象クラス、抽象メソッド、抽象プロパティを使用する方法を示します。また、Shape 抽象クラスは、具体的なエンティティである円および正方形の共通の要素を表します。すべての図形 (2 次元の座標系) に共通する特徴は、Shape クラスに抽象化されています。これらは、グリッドの位置、回転の角度、および面積と周囲のプロパティです。位置以外はオーバーライドできます。個々の図形では位置の動作を変更できないため、位置はオーバーライドできません。
Circle クラスと同じように、回転メソッドはオーバーライドできます。このクラスは対称であるため、回転による変化はありません。そのため、Circle クラスでは、回転メソッドの代わりに、何も実行しないメソッドが使用されます。
// An abstract class that has some methods and properties defined
// and some left abstract.
[<AbstractClass>]
type Shape2D(x0 : float, y0 : float) =
let mutable x, y = x0, y0
let mutable rotAngle = 0.0
// These properties are not declared abstract. They
// cannot be overriden.
member this.CenterX with get() = x and set xval = x <- xval
member this.CenterY with get() = y and set yval = y <- yval
// These properties are abstract, and no default implementation
// is provided. Non-abstract derived classes must implement these.
abstract Area : float with get
abstract Perimeter : float with get
abstract Name : string with get
// This method is not declared abstract. It cannot be
// overriden.
member this.Move dx dy =
x <- x + dx
y <- y + dy
// An abstract method that is given a default implementation
// is equivalent to a virtual method in other .NET languages.
// Rotate changes the internal angle of rotation of the square.
// Angle is assumed to be in degrees.
abstract member Rotate: float -> unit
default this.Rotate(angle) = rotAngle <- rotAngle + angle
type Square(x, y, sideLengthIn) =
inherit Shape2D(x, y)
member this.SideLength = sideLengthIn
override this.Area = this.SideLength * this.SideLength
override this.Perimeter = this.SideLength * 4.
override this.Name = "Square"
type Circle(x, y, radius) =
inherit Shape2D(x, y)
let PI = 3.141592654
member this.Radius = radius
override this.Area = PI * this.Radius * this.Radius
override this.Perimeter = 2. * PI * this.Radius
// Rotating a circle does nothing, so use the wildcard
// character to discard the unused argument and
// evaluate to unit.
override this.Rotate(_) = ()
override this.Name = "Circle"
let square1 = new Square(0.0, 0.0, 10.0)
let circle1 = new Circle(0.0, 0.0, 5.0)
circle1.CenterX <- 1.0
circle1.CenterY <- -2.0
square1.Move -1.0 2.0
square1.Rotate 45.0
circle1.Rotate 45.0
printfn "Perimeter of square with side length %f is %f, %f"
(square1.SideLength) (square1.Area) (square1.Perimeter)
printfn "Circumference of circle with radius %f is %f, %f"
(circle1.Radius) (circle1.Area) (circle1.Perimeter)
let shapeList : list<Shape2D> = [ (square1 :> Shape2D);
(circle1 :> Shape2D) ]
List.iter (fun (elem : Shape2D) ->
printfn "Area of %s: %f" (elem.Name) (elem.Area))
shapeList