抽象クラス

"抽象クラス" は、一部またはすべてのメンバーを実装しないままにして、派生クラスによって実装できるようにするためのクラスです。

構文

// 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 キーワードでマークされている場合は、メンバーが、その型の仮想関数の内部テーブルに "仮想ディスパッチ スロット" と呼ばれるエントリを持っていることを示します。 つまり、メソッドは仮想ですが、F# では virtual キーワードは使用されません。 キーワード abstract は、そのメソッドが実装されているかどうかに関係なく、仮想メソッドに対して使用されます。 仮想ディスパッチ スロットの宣言は、そのディスパッチ スロットのメソッドの定義とは別個のものです。 したがって、別の .NET 言語の場合に相当する F# での仮想メソッドの宣言と定義は、default キーワードまたは override キーワードのいずれかを使用した抽象メソッド宣言と別個の定義の両方を組み合わせたものになります。 詳細と例については、「メソッド」をご覧ください。

クラスは、宣言されているが定義されていない抽象メソッドがある場合にのみ、抽象と見なされます。 したがって、抽象メソッドを持つクラスは、必ずしも抽象クラスではありません。 クラスに未定義の抽象メソッドがある場合を除き、AbstractClass 属性は使用しないでください。

上記の構文で、accessibility-modifier には publicprivate、または internal を指定できます。 詳しくは、「アクセス制御」をご覧ください。

他の型と同様に、抽象クラスは、1 つの基底クラスと 1 つ以上の基底インターフェイスを持つことができます。 各基底クラスまたはインターフェイスは、inherit キーワードと共に別個の行に指定します。

抽象クラスの型定義には、完全に定義されたメンバーを含めることができますが、抽象メンバーを含めることもできます。 抽象メンバーの構文は、上記の構文で別個に示されています。 この構文で、メンバーの "型シグネチャ" は、順番に並べたパラメーター型と戻り値の型を、カリー化およびタプル化パラメーターに応じて -> トークンまたは * トークンで区切ったものを含むリストです。 抽象メンバーの型シグネチャの構文は、シグネチャ ファイルで使用されているもの、および Visual Studio Code エディターで IntelliSense によって示されるものと同じです。

次のコードは、抽象クラス Shape と、2 つの抽象ではない派生クラス Square と Circle の例を示しています。 この例は、抽象クラス、メソッド、プロパティの使用方法を示しています。 この例で、抽象クラス Shape は、具象エンティティである円と正方形に共通の要素を表しています。 すべての図形の (2 次元座標系における) 共通の特徴が、Shape クラスに抽象化されています。グリッド上の位置、回転角度、面積、外周の各プロパティです。 これらのうち位置だけは個々の図形で動作を変更することはできませんが、それ以外はオーバーライドできます。

rotation メソッドは、Circle クラスのように、オーバーライドすることができます。円は対称形であるため、回転させても変化しません。 そのため、Circle クラスでは、rotation メソッドは何も実行しないメソッドに置き換えられています。

// 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
    // overridden.
    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

出力:

Perimeter of square with side length 10.000000 is 40.000000
Circumference of circle with radius 5.000000 is 31.415927
Area of Square: 100.000000
Area of Circle: 78.539816

関連項目