WisdomSoft - for your serial experiences.

5.5 抽象クラス

宣言だけで定義(本体)を含まない抽象メソッドを持つクラスを抽象クラスと呼びます。抽象クラスは、サブクラスによって抽象メソッドが実装されることを前提とした未完成なクラスです。

5.5.1 宣言だけのメソッド

クラスは継承によって拡張することができるため、その基本となるスーパークラスは、できるだけ多くの可能性を残すために汎用的なコードのみを提供するべきであると考えられます。スーパークラスの処理が、特定のプロジェクトに依存したり、固定的な用途にしか使えない場合、拡張することが難しくなります。これでは、オブジェクト指向プログラミングの可能性を自ら否定しているも同然でしょう。

しかし、多くの用途に使えるように抽象化を試みると、インスタンス化できるほどの情報を保持できない場合があります。そこで、あらかじめサブクラス化を予想し、あえて不完全なクラスを作成することで抽象化を実現することができます。このように、インスタンス化することができない不完全なクラスを抽象クラスと呼びます。

抽象クラスを宣言するには abstract 修飾子をクラスの宣言で指定します。クラスの宣言では class キーワードの前に修飾子を指定することができます。abstract 修飾子を指定して宣言された抽象クラスは、特典として抽象メソッドが宣言できるようになります。抽象メソッドとは、abstract 修飾子が指定されている、オーバーライドされることを目的とした本体を持たないメソッドです。抽象メソッドは宣言しか行われず、実体はサブクラスに委ねられます。

抽象クラスと抽象メソッド
abstract class クラス名 {
	abstract 戻り値型 メソッド名(仮パラメータリスト);
}

抽象メソッドを持つクラスは、インスタンスを生成することができないので抽象クラスでなければなりません。通常のクラスに抽象メソッドが宣言された場合はコンパイル・エラーとなります。抽象メソッドは、上記した宣言を見てわかるように abstract 修飾子を指定して宣言します。ただし、本体は記述せずにセミコロン ; で終了します。これは、いわばメソッドの予約です。

抽象クラスを継承するサブクラスは、抽象メソッドを必ずオーバーライドしなければなりません。抽象メソッドがサブクラスによってオーバーライドされた状態を、サブクラスによって実装されていると表現します。抽象クラスの役割は、クラスの利用者に対して実体を隠蔽しながら操作を保証することにあります。開発者は具体的な処理を抽象クラスのサブクラスで実装し、一方で利用者には抽象クラスだけを見せることでプログラムを簡素化することができます。

コード1
abstract class A {
	abstract public void show();
}

class B extends A {
	public void show() { System.out.println("B.show"); }
}

class C extends A {
	public void show() { System.out.println("C.show"); }
}

class Test {
	public static void main(String args[]) {
		//showA(new A()); //インスタンスが作れない
		showA(new B());
		showA(new C());
	}
	public static void showA(A value) {
		value.show();
	}
}
実行結果
>java Test
B.show
C.show

コード1は、抽象メソッド show() を宣言する抽象クラス A を作成しています。抽象メソッドを宣言するクラスは、必ず抽象クラスでなければならないので A クラスには abstract 修飾子が指定されています。A クラスの直接のサブクラスは、必ず show() メソッドを実装しなければならず、オーバーライドしない場合はやはり抽象クラスとして宣言しなければなりません。

B クラスと C クラスは、A クラスを継承する直接のサブクラスです。これらのクラスは抽象クラスではないので show() メソッドをオーバーライドしなければなりません。メソッドが実装されることによって、不完全だった抽象クラスが完成し、インスタンスを作成することができます。B や C 型の変数は抽象クラスである A 型にワイドニング変換することができます。A 型の変数は抽象クラスが宣言するメソッドからオーバーライドした実装のメソッドにアクセスすることができるため、B クラスのインスタンスと C クラスのインスタンスを、まったく同じ方法で制御することができるのです。

Test クラスの showA() メソッドでは A 型の参照を受け取り A.show() メソッドを呼び出します。これはすなわち、A クラスを実装するすべてのサブクラスを渡すことが可能であり、実体がどのようなものであれ、メソッドの呼び出しは保証されていることになるのです。クラスの開発者はまだ見ぬ A クラスの実装を抽象的に制御することができるため、将来の拡張や他の開発チームの仕事を予想しながらプログラムを組み立てることができます。

このプログラムの A クラスは抽象メソッドだけを宣言していますが、抽象クラスに普通のフィールドやメソッドを宣言しても問題はありません。抽象クラスは、抽象メソッドの宣言が可能であることと、インスタンス化できないことを除いて通常のクラスと同じです。サブクラスに提供するべき基本的な機能を通常のメソッドとして実装できますし、サブクラスから明示的に呼び出されることを想定したコンストラクタを記述することもできます。