抽象クラス(Abstract Class)

抽象クラスは文字通り抽象的存在のクラスであり具体的な処理はこれを継承したクラスに記述させます。抽象クラスの存在意義は複数のクラスに対して共通性を持たせることであり、クラス設計においてとても重要な役割を担っています。


抽象クラスの特徴

抽象クラスの特徴として、具象クラス(通常のクラス)との違いを挙げます。


1. 抽象メソッドを定義する事ができる。
抽象メソッドとは、実際の処理を自身にではなく子クラスに記述させるためのメソッドです。この抽象メソッドを記述できることが、抽象クラスの最大の特徴です。抽象クラスを継承したクラスは、この抽象メソッドを必ず「オーバーライド」しなければなりません。(オーバーライドしないとコンパイルエラーとなります。)
2. 抽象クラス単体でインスタンスを生成する事はできません。
抽象メソッドを定義している。つまり、実際の処理を記述していない訳ですから当然インスタンスを生成して使用する事は出来ません。

抽象クラス及び抽象メソッドの書式

抽象クラス及び抽象メソッドの書式は次の通りです。

abstract class クラス名 {
abstract 戻り値 メソッド名(引数リスト) ;
}

※抽象クラスには通常のメソッドも記述できます。また抽象メソッドは省略可能(抽象クラスの意味がないですが)です。

▲PageTop

抽象クラスの設計

抽象クラスについては、実際の使用例を見たほうが理解しやすいと思いますので、例として、とある会社の社員クラスの設計を考えてみます。


社員クラスの条件は次の通りです。

  1. 午前9時に出社する。
  2. 会社には、営業部、開発部、総務部がありそれぞれ、仕事を開始する。
  3. 午後6時に帰宅する。

このとき社員クラスを次のように考えたとすると・・・

抽象クラス例題イメージ1

これでは働くという処理に対して、営業部、開発部、総務部の区別をつける事が出来ません。これをどう解決するか?

次の2つが考えられます。

  1. 「営業部社員クラス」、「開発部社員クラス」、「総務部社員クラス」を個別に作成する。
  2. 「働く()」に引数を持たせて、その引数の値で営業部、開発部、総務部を判別し処理を分ける。若しくは「働く()」の代わりとして各部署用にメソッドを作成(例えば、営業ワークメソッド、開発ワークメソッド等)して呼び分ける。

この2つの解決方法に対するメリットとデメリットとしては、次のようなことが挙げられます。


各解決方法のメリットとデメリット
メリット デメリット
1の解決方法 各クラスの仕様の変化やクラスの追加などに対して他のクラスへの影響を抑えることができる。 各クラス共通の処理も個別に記述しなければならない。また各クラスを個別に記述することで統一性(メソッドのシグニチャなど)が保たれる保障がない。ミスをしてもコンパイル段階では判別できない。
2の解決方法 各部署の社員クラスを社員クラス1つで呼び出せるので、各クラスを使用する側からみて取り扱いが楽になる。 1つの修正が全体に影響を及ぼす危険性が増える。

どちらを選択したらよいでしょうか・・・?


実はこのような時に抽象クラスを使用すると、その威力を発揮してくれます。

社員クラスを抽象クラス、そして「働く()」を抽象メソッドとして次の様な構成を考えます。

抽象クラス例題イメージ2

これにより上記2つの解決方法で、その両方のメリットを担保しつつ、それぞれのデメリットが解消されているのが解るでしょうか。

つまり次のようになります。

  1. 各部署の社員クラスを個別に作成するが、社員クラスを継承するこで共通メソッドは記述せずに使用できる。(1のメリット担保、1・2のデメリット解消)
  2. 抽象メソッドである「働く()」の実装は保障される。(1のデメリット解消)
  3. 各部署の社員クラスは、親クラスである「社員クラス型」で統一した呼び出し方ができる。(2のメリット担保)

このように抽象クラスはクラスの構成を考える上でとても有効な手段となることが理解できると思います。

実際のソースを次に示しますので動作確認してください。


・サンプルソース(Sample0901.java)

abstract class Employee0901 {
protected String name="社員";
abstract void work();  // 抽象メソッド
public void goOffice(){
System.out.println(name + "が出社しました。");
}
public void goHome(){
System.out.println(name + "が帰宅しました。");
}
}

class SalesMan0901 extends Employee0901 {
public SalesMan0901(String name) {
super.name = name;
}
public void work() {
System.out.println("営業の仕事を行います。");
}
}

class Developer0901 extends Employee0901 {
public Developer0901(String name) {
super.name = name;
}
public void work() {
System.out.println("開発の仕事を行います。");
}
}

class GeneralManager0901 extends Employee0901 {
public GeneralManager0901(String name) {
super.name = name;
}
public void work() {
System.out.println("総務の仕事を行います。");
}
}

public class Sample0901 {
public static void main(String[] args) {
// 営業部の社員
Employee0901 sm = new SalesMan0901("営業部の社員A");  // 抽象クラスを参照型として使用できます。
// 開発部の社員
Employee0901 dp = new Developer0901("開発部の社員B");  // 抽象クラスを参照型として使用できます。
// 総務部の社員
Employee0901 gm = new GeneralManager0901("総務部の社員C");  // 抽象クラスを参照型として使用できます。
// 出社
sm.goOffice();
dp.goOffice();
gm.goOffice();
// 業務
sm.work();
dp.work();
gm.work();
// 帰宅
sm.goHome();
dp.goHome();
gm.goHome();
}
}

・実行結果

C:\dev\java>javac sample0901.java [Enter]

C:\dev\java>java sample0901 [Enter]
営業部の社員Aが出社しました。
開発部の社員Bが出社しました。
総務部の社員Cが出社しました。
営業の仕事を行います。
開発の仕事を行います。
総務の仕事を行います。
営業部の社員Aが帰宅しました。
開発部の社員Bが帰宅しました。
総務部の社員Cが帰宅しました。
        

▲PageTop