4-3 抽象クラスの役割


・ クラスの継承を実現する途中で、 「抽象クラス(abstract class)」と呼ばれるクラスが現れることがあります。 通常のクラスの目的は、もちろんオブジェクトを生成し活用することです。 ところが、抽象クラスは直接自分自身のオブジェクトを生成しません。 他のクラスのスーパークラスになることを主要な目的としています。
たとえば「哺乳類」というクラスを考えてみましょう。 「哺乳類」のサブクラスには「象」、「馬」、「キリン」などが存在します。 具体的な一匹一匹の個体をオブジェクトと考えます。 この場合、 そして「哺乳類」に含まれるオブジェクトは必ずサブクラスのいずれかになります。 「哺乳類」自身の直接のオブジェクトは存在しません。 ただし「哺乳類」のクラスに何も情報が定義されていないというわけではありません。 たとえば「乳を飲ませて子供を育てる」という共通の手続きは、 「哺乳類」のクラスで定義されることになるでしょう。 クラスの継承関係を数学の「集合」のような形式で 表現で記述してみると理解しやすいかもしれません。


クラスの継承関係の表現(その2)

このように抽象クラスは、 「共通の性質を持つクラスをグループ分けする」という働きをします。 そして、そのグループに共通する性質を定義を記述することで、 個々のサブクラスの記述を簡略化することが可能になります。

・ 次に Javaにおける抽象クラスの取り扱いを調べてみましょう。 まず「キーワード abstract」と 「abstract宣言されたメソッド」から説明します。 Javaでは次のように名前と型の情報だけで、 定義の部分が存在しないメソッドを記述することができます。


public abstract void hasNothig( int x, int y );
 

もちろん定義がないままでは何も処理が実行できません。 このままでは、このメソッドを呼び出しても何の意味もないでしょう。 では、このようなメソッドはどうやったら利用できるのでしょうか?  今回のテーマが「クラスの継承」であったことを思い出してください。 あるクラスの中では abstract宣言されたメソッドとして存在していても、 そのサブクラスで再定義することによって処理の中身を持つことが可能です。 abstract宣言されたメソッドも最終的には中身を持つことになるわけです。
このように「クラスにあらかじめ予定されている機能と、 その実際の内容(実装)を分離する」という考え方は、 最初のうちは馴染みにくいかもしれません。 しかし Javaでクラスを設計する場合にはしばしば用いられる重要なテクニックです。

abstract宣言されたメソッドを1つでも含むクラスは、 まだ「未完成な」部分を持っていることになります。 こうしたクラスは自動的に抽象クラスとなります。 抽象クラスの定義の先頭の部分には、 メソッドと同じように修飾子 abstractを必ず付けなけなければなりません。 abstract宣言されたメソッドを持たなくても クラスの定義で abstract修飾子を 付ければ、やはり抽象クラスとなります。 なお、抽象クラスは一般に通常のクラスと同じようにクラス定義の中身を持っています。abstract宣言されたメソッド以外にも通常のメソッドが存在し、 フィールド、そしてコンストラクタも定義されているのが普通です。


public abstract class UnfinishedClass {
                     :
                     :

・ 抽象クラスと 抽象クラスでない通常のクラスとでは何が異なるのでしょうか? 一番大きな違いは、「抽象クラスはオブジェクトを生成できない」 という点です。 ただし「オブジェクトを生成できない」という制限を、 「コンストラクタを定義できない」という意味と混同してはいけません。 抽象クラスであってもコンストラクタを定義することができます。 次のようなサンプルを見てみましょう。


public abstract class FirstClass {
     public FirstClass( int x ) {
           this.x = x;
     }
     public int x;
     public abstract int doSomething();
}
 
public class SecondClass extends FirstClass {
       public SecondClass( int x, int y ){
              super( x );
              this.y = y;
       } 
       public int y;
       public int doSomething(){
              return x+y;
       } 
}

FirstClassは抽象クラスですがコンストラクタを定義しています。 また、このコンストラクタはサブクラスの SecondClassのオブジェクトを生成する ために必須です。 ただし次のように FirstClassのオブジェクトを生成するために 直接コンストラクタを呼び出そうとすると、 コンパイル時にエラーとなります。


public class ThirdClass {
       public static void main( String argv[] ) {
              FirstClass fc = new FirstClass( 1 );   // エラー
              SecondClass sc = new SecondClass( 1, 1 );
       } 
}

クラスの継承を利用して段階を踏んでクラスを設計する場合、 上のように途中に抽象クラスが定義されることは決して珍しいことではありません。