WisdomSoft - for your serial experiences.

抽象クラス

抽象クラスは、メソッドの宣言のみで本体を持たない抽象メソッドを持つことができます。抽象メソッドは派生クラスによってオーバーライドされることが前提のメソッドで、メソッド名、パラメータリスト、戻り値だけを宣言し、処理内容は派生クラスに委ねられます。

不完全なクラス

あるオブジェクトの共通する基本クラスを作成するとき、基本クラスの時点ではインスタンス化するほど完成していない場合もあります。基本クラスでは具体的な動作を実装せずに、派生クラスでオーバーライドすることを前提として設計した場合、基本クラスのインスタンスは未完成で意味を持ちません。このような場合、基本クラスではメソッドを宣言だけにとどめ、本体は派生クラスで実装するという方法があります。

具体的な機能の実装を派生クラスに委ね、一部のメソッドの実装を持たない不完全なクラスを抽象クラス(abstract class)と呼びます。抽象クラスは、その時点では機能の一部が未実装の状態で、派生クラスによってオーバーライドされることによって完成します。よって、抽象クラスを new 演算子でインスタンス化することはできません。

抽象クラスを宣言するには、クラス宣言で abstract 修飾子(abstract modifier)を指定します。

抽象クラス
abstract class クラス名 ...

抽象クラスは継承されることが前提となるため sealed 修飾子と組み合わせることはできません。

abstract class A
{
}

class B : A
{
}

上記コードの A クラスは abstract 修飾子で宣言されているため抽象クラスです。B クラスは A クラスを継承する非抽象クラスです。この場合 B クラスのインスタンスを生成し A 型として扱うことで、詳細な実装を派生クラスに委ねながら、外部からは A クラス型として操作できます。すなわち、実装の抽象化です。

A obj1 = new A() //エラー
A obj2 = new B() //OK

抽象クラスは高度なオブジェクト指向設計に必要不可欠な機能であり、機能の実装と、外部からの機能の操作を分離し、オブジェクトが持つ振る舞いを隠蔽できます。

コード1
abstract class MagicalGirl
{
    public virtual void Chant() { }
}

class Nanoha : MagicalGirl
{
    public override void Chant()
    {
        System.Console.WriteLine("風は空に、星は天に、不屈の心はこの胸に!");
    }
}

class Sample
{
    static void Main(string[] args)
    {
        MagicalGirl nanoha = new Nanoha();
        nanoha.Chant();
    }
}
実行結果
コード1 実行結果

コード1の MagicalGirl クラスは抽象クラスなので、それ自身をインスタンス化することはできません。Nanoha クラスは MagicalGirl クラスを継承しているため、MagicalGirl 型のオブジェクトとして扱うことができます。このように、抽象クラスは派生クラスによって実装され、派生クラスのインスタンスを抽象化する目的に用いられます。

この MagicalGirl クラスでは本体が空の Chant() メソッドが用意されていますが、このようなメソッドを持つことは意味がありません。そこで、抽象クラスは派生クラスでオーバーライドすることが前提の、本体を持たない空のメソッドを宣言できます。

抽象メソッド

抽象クラスは抽象メソッド(abstract method)を宣言できます。 抽象メソッドはメソッドの宣言のみを行い、本体を記述しません。抽象メソッドは派生クラスでオーバーライドすることで実装します。抽象メソッドを宣言するには、メソッド宣言で abstract 修飾子を指定します。この場合、メソッドは本体 { } を記述することはできません。

抽象メソッド
abstract 戻り値型 メソッド名 (パラメータリスト) ;

オーバーライドされることが前提の抽象メソッドは仮想メソッドの性質も合わせ持ちます。そのため virtual 修飾子と組み合わせることはできません。

抽象メソッドを持つ基本クラスを継承した派生クラスは、抽象メソッドをオーバーライドする必要があります。そのクラスが抽象メソッドをオーバーライドしない場合は、そのクラスも抽象クラスとして宣言しなければなりません。抽象メソッドを実装しない非抽象クラスはコンパイルエラーになります。

abstract class A
{
	public abstract void M();
}
class B : A //OK。M() メソッドを実装している
{
	public override void M() {} //M() の実装
}
class C : A {} //エラー。M() メソッドを実装していない
abstract class D : A {} //OK。抽象クラス

したがって、抽象クラスを継承する派生クラスは、抽象メソッドをすべてオーバーライドするか、または自身も抽象クラスになるかのいずれかしかありません。最終的には、何らかの派生クラスですべての抽象メソッドが実装されなければ、抽象クラス型のインスタンスを生成することはできません。

コード2
abstract class MagicalGirl
{
    public abstract void Chant();
}

class Nanoha : MagicalGirl
{
    public override void Chant()
    {
        System.Console.WriteLine("風は空に、星は天に、不屈の心はこの胸に!");
    }
}
class CardCapter : MagicalGirl
{
    public override void Chant()
    {
        System.Console.WriteLine("闇の力を秘めし鍵よ、真の姿を我の前に示せ。");
    }
}

class Sample
{
    static void Main(string[] args)
    {
        MagicalGirl magicalGirl = new Nanoha();
        magicalGirl.Chant();

        magicalGirl = new CardCapter();
        magicalGirl.Chant();
    }
}
実行結果
コード2 実行結果

コード2の MagicalGirl クラスの Chant() メソッドは abstract 修飾子で宣言された抽象メソッドです。よって、MagicalGirl クラスを継承する派生クラスは、それが非抽象クラスの場合 Chant() メソッドをオーバーライドしなければなりません。

抽象クラスと抽象メソッドによって、具現的な実装から分離された抽象的な概念としてオブジェクトを操作できるようになります。