5-2: Javaにおける継承の記述


・ 具体的なサンプルを通じて、 Javaではクラスの継承がどのように実現されるかを見てみましょう。 あるクラスを継承して新しいクラスを設計するためには、 クラスの定義の先頭でクラス名の後に extends というキーワードを用います。 extends の後にはスーパークラス名が来ます。


public class Elephant extends Animal {
                  :
                  :
 

この記述によって Animalというスーパークラスから Elepahtとというサブクラスが作られ、 Animalの持つ性質はすべて Elephantに継承されます。 情報を継承するためのルールをもう少し厳密にまとめると、次のようになります。

  1. スーパークラスで既に定義されたフィールドやメソッドは、 サブクラスの定義内に改めて記述しなくても利用できる(これが継承の主目的)。
  2. サブクラスの定義内に独自に新しく追加されたフィールドやメソッドは、 そのまま追加される(当たり前)。
  3. サブクラスの定義内に、スーパークラスで既に定義されたのと同じ名前の フィールドやメソッドが定義された場合は、 サブクラスで再定義したものが優先される(つまり古い定義は上書きされる)。

最後のルールについては多少補足しておきましょう。 上書きされた情報は消えてしまうわけではありません。 スパークラスで定義されていたフィールドやメソッドを呼び出したい時には super というキーワードが利用できます。 thisが自分自身のクラスを表したように、superは直接のスーパークラスを表します。 superの使用は決して特別なことではありません。 特にメソッドを再定義する場合、 スーパークラスの作業に何かつけ加える場合が多いでしょう。 superの記述方法を利用すれば、最初から全て定義し直す手間が省けます。


/** スーパークラスで定義されたメソッドに作業を追加する */
 
public void doSomething() {
 
       super.doSomthing();   // スーパークラスの作業
                :
      // サブクラスで追加する作業
                :
 

super はコンストラクタの記述の中でしばしば現れます。 継承するデータの初期化の処理などを行なうためです。 サブクラスのコンストラクタの中で スーパークラスのコンストラクタを呼び出すには、 次のように super() という記述を使用します。


/** サブクラスのコンストラクタ */
public SubClass() {
 
       super();  // スパークラスのコンストラクタの呼び出し
                :
      // サブクラスで追加する作業
                :
 

一般にコンストラクタは引数の個数や型によって複数定義されています。 super()に引数を渡す場合には、当然スーパクラスの側に、その引数の指定に 合致するコンストラクタの定義が存在する必要があります。
スーパクラスに明示的にコンストラクタが定義されていない場合でも、 引数なしのコンストラクタの呼び出し super()は呼び出すことが可能です。 (「暗黙のコンストラクタ」と呼ばれます。) ただし、スーパクラスにコンストラクタの定義が存在し、しかも引数なしの コンストラクタが定義されていない場合には引数なしの super()は呼び出せ ません。

クラスの継承は何代に渡ってもかまいません。 たとえば先ほどの Elephant の更にサブクラスを作成することもできます。


public class AfricanElephant extends Elephant {
                  :
                  :
 

AfriacanElephant の直接のスーパークラスは Elephant ですが、 Animal もまた AfriacanElephant のスーパークラスです。 AfricanElephant は Animalからも性質を継承することになります。 キーワード superを使ってたどりたければ、super.super のような指定が可能です。
あるクラスのオブジェクトは、 そのスーパークラスにも所属すると考えることができます。 上の継承関係で言えば、AfricanElephant のオブジェクトは Elephant でもあり Animalでもあるということです。 実際 Javaのプログラムでは、 あるクラスのオブジェクトをそのスーパークラスを参照するための変数に キャストなしに代入することが可能です。


Animal animal;
Elephant elephant;
AfricanElephant africanElephant;
elephant = africanElephant;
animal = elephant;
 

この逆を行う場合には明示的な「キャスト」が必要となります。 Javaのキャストは Cと同じで型(クラス)名を ( ) で囲み、 オブジェクト変数の前に付けることで表現します。


Animal animal;
Elephant elephant;
AfricanElephant africanElephant;
elephant = (Elephant)animal;
africanElephant = (AfricanElephant)elephant;

・ さて Javaにおけるクラスの継承を完全に理解するためには、 もう一つ知っておかなければならない事実があります。 それは Object というクラスの存在です。 Objectは Javaで定義される全てのクラスの共通のスパークラスとなります。 クラスの継承を用いずにクラスの定義を記述したつもりでも、 実は自動的に Objectのサブクラスになるのです。 つまり、次の2つの記述は全く同じことを表していることになります。


public class NewClass {
                 :
                 :
 
public class NewClass extends Object {
                 :
                 :
 

Objectクラスのオブジェクトをそのまま利用することは普通ありません。 しかし、Javaのプログラムの中には、Objectという記述がしばしば登場します。 その場合の Objectは、メソッドの引数や返値でやり取りされる データのクラスがはっきり決まっていない(何でも許される)という意味です。 どんなクラスも必ず Objectクラスのサブクラスであることから、 すべてのオブジェクトは Objectクラスに所属すると見なすことができるからです。 (ただし原始型のデータは Object によって表わすことはできません。)


public class Animal {
    public void eatSomething( Object food ) {
                       :
                       :

また、Objectは全ての配列のオブジェクトのスーパクラスでもあります。 したがって「任意の型の配列」を引数などの渡したい場合は Objectを用います。 (Object[] は「任意の型の配列」にはなれません。intなどの原始型の配列は 含まないからです。)