【改訂版】Eclipseではじめるプログラミング
連載インデックスへ
【改訂版】Eclipseではじめるプログラミング(12)

継承やオーバーライドで簡単にクラスを“拡張”しよう


株式会社ガリレオ
小山博史
2009/11/13


自作クラスを継承・拡張してみよう

 クラスの拡張をするに当たっては、メンバー(フィールドとメソッド)の実装継承と、実装の再定義(オーバーライド)について理解をする必要があります。

「スーパークラス」と「サブクラス」、そして「継承」とは

 次の例では、クラスBaseを拡張して、クラスAを作成しています。クラスBaseのことを、クラスAの「スーパークラス」「親クラス」と呼びます。また、クラスAのことを、クラスBaseの「サブクラス」「子クラス」と呼びます。このように、クラス名の後に、extendsを書いて、そのクラスのスーパークラスとなるクラス名を記述することにより、クラスの拡張ができます。

  クラス拡張の宣言方法
class A extends Base {
    // 拡張のためのコード
}

 クラスを拡張することにより、クラスAはクラスBaseが公開しているフィールドやメソッドを実装していることになります。このことを、「クラスAはクラスBaseのフィールドとメソッドを継承(inherit)している」と表現します。このため、「Aには追加したいフィールドやメソッドについてのみコーディングをすればいい」ということになります。

 また、サブクラスはスーパークラスがサブクラスへアクセスを許しているフィールドやメソッドを利用できます。例えば、BaseクラスがbaseFieldフィールドを持ち、baseMethodメソッドを実装して、両方ともサブクラスが使えるように宣言していたとすると、AクラスはbaseFieldフィールドへアクセスできますし、baseMethodメソッドを呼び出すこともできます。この辺りの話は次回以降の記事で取り上げます。ここでは、そういうものがあるという程度の理解で結構です。

コラム 「次回で詳しく解説する“アクセス制限”とは」

アクセス制限については、これまで説明をしていませんが、次回以降の記事で予定しています。詳細はそのときに説明しますので、ここでは簡単な説明だけをしておきます。

これまで「public」というキーワードがついているメソッドやフィールドがあったと思いますが、これは、どのクラスにも公開されていることを意味します。「private」というキーワードがついているメソッドやフィールドは、そのクラスでしか使えません。「protected」というキーワードがついているメソッドやフィールドは、そのクラスのサブクラスがアクセスできます。public、private、protectedが付いていないメソッドやフィールドについては、同一パッケージ内のクラスからアクセスができます。この4点が基本です。

自作クラスを拡張するには

 例として、sample12.Baseクラスを用意し、これをスーパークラスとして利用するsample12.Aクラスを用意してみましょう。文法を理解するためのものなので、実用性はまったくありませんが、ここまでの説明を理解する役には立ちます。単純にbaseFieldフィールドを宣言して1で初期化し、baseMethodメソッドは常にfalseを返すよう実装しています。

  sample12/Base.java
package sample12;
class Base {
    int baseField = 1;
  
    boolean baseMethod() {
        return false;
    }
}

 sample12.Aクラスはsample12.Baseクラスを拡張して、aFieldフィールドとaMethodメソッドを追加しています。このメソッドでは、スーパークラスであるsample12.BaseクラスからbaseFieldフィールドとbaseMethodメソッドを継承しているため、ほかのクラスから使えるようになっています。sample12.Aクラスの中にあるメソッドでもbaseFieldフィールドとbaseMethodメソッドは利用できるので、aMethodメソッド内で使っています。

  sample12/A.java
package sample12;
class A extends Base {
    int aField = 10;
    boolean aMethod() {
        //スーパークラスのフィールドとメソッドを利用
        System.out.println("aMethod:baseField:" + baseField);
        return !baseMethod();
    }
}

 処理内容は単純ですが、念のため確認をしておきます。「"aMethod:baseField:" + baseField」を出力して、「!baseMethod()」の計算結果を返すという処理です。baseMethod()は、sample12.Baseクラスから継承したメソッドなので、必ず「false」となりますから、「!baseMethod()」は常に「true」となります。

注釈 「『論理否定』って何か分かってる?」

本連載ではこれまで出てきませんでしたが、「!」は「論理否定」の単項演算子で、後に続く値を否定します。つまり、「!false」は「true(falseではない)」となり、「!true」は「false(trueではない)」となります。

 最後に、これらを利用するsample12.Sample02クラスを作成します。起動メソッド(mainメソッド)を持ち、その中でsample12.Baseクラス、sample12.Aクラスのインスタンスを生成し、それぞれが持つフィールドにアクセスをしたり、メソッドを呼び出したりしています。

  sample12/Sample02.java
package sample12;
public class Sample02 {
    public static void main(String[] args) {
        Base base = new Base();
        A a = new A();
    
        // Baseクラスのフィールドとメソッド
        System.out.println("base.baseField   :" + base.baseField);
        System.out.println("base.baseMethod():" + base.baseMethod());
        // AクラスはBaseクラスと同じフィールドとメソッドを持つ
        System.out.println("a.baseField      :" + a.baseField);
        System.out.println("a.baseMethod()   :" + a.baseMethod());
        // Aクラス独自フィールドへのアクセス
        System.out.println("a.aField         :" + a.aField);
        // Aクラス独自メソッドの呼び出し
        System.out.println("a.aMethod()      :" + a.aMethod());
        // baseとaは別インスタンスなので、
        // base.baseFieldとa.baseFieldの値は別になる
        base.baseField = 3;
        System.out.println("base.baseField   :" + base.baseField);
        System.out.println("a.baseField      :" + a.baseField);
        a.baseField = 30;
        System.out.println("base.baseField   :" + base.baseField);
        System.out.println("a.baseField      :" + a.baseField);
    }
}

 実行結果は、次のようになります。

  sample12.Sample02クラスの実行結果
base.baseField   :1
base.baseMethod():false
a.baseField      :1
a.baseMethod()   :false
a.aField         :10
aMethod:baseField:1
a.aMethod()      :true
base.baseField   :3
a.baseField      :1
base.baseField   :3
a.baseField      :30

 sample12.AクラスにはbaseFieldフィールドやbaseMethodメソッドの宣言はありませんが、きちんと使えていて、Baseクラスのものと同じ結果になっていることが分かります。sample12.Aクラス独自のフィールドへはアクセスできて「a.aField :10」という結果が出ています。aMethodメソッドの呼び出しにより、「aMethod:baseField:1」が出力されてから、「a.aMethod() :true」という正しい結果も出力されています。

 baseとaは別インスタンスなので、base.baseFieldとa.baseFieldの値は別になっています。base.baseFieldが3になってもa.baseFieldの値は1のままですし、a.baseFieldの値を30にしても、base.baseFieldには影響がありません。

クラスを拡張する際の5つの注意点

 どうでしょうか、クラスの拡張をすることにより、サブクラスはスーパークラスのフィールドやメソッドをあらためてコーディングしなくてもいいことが理解できたでしょうか。さて、クラスを拡張するに当たっては、次の項目について検討が必要です。1.と2.については、実装の仕方はいま確認をしたところなので、いいでしょう。ここからは、残りの項目について順番に見ていくことにします。

  1. どういったフィールドを追加するか
  2. どういったメソッドを追加するか
  3. クラスの振る舞いを変えるか
  4. どういったコンストラクタを用意するか
  5. 同値性についてどうするか

コラム 「実は、あなたも継承しているObjectクラス」

すべてのクラスは、Objectクラスを拡張していることを最初に書きました。これはJava APIドキュメントを見ると分かりますが、どのクラスもスーパークラスをたどっていくと、Objectクラスにたどり着くのです。実は、Javaでは、クラス宣言時にextendsを使ってスーパークラスを指定しない場合は、暗黙のうちにObjectクラスを拡張します。ですから、equalsメソッドやhashCodeメソッドなどはメソッドを実装しなくても使えるようになっています。試しに、sample12.Baseクラスでこれらのメソッドが使えるかサンプルコードを書いて実行してみるといいでしょう。次のように書くことができ、コンパイルエラーは出ません。実行もできます。

  ソースコード例
package sample12;
public class SampleBase {
    public static void main(String[] args) {
        Base base = new Base();
        System.out.println("base.equals(null):" + base.equals(null));
        System.out.println("base.hashCode()  :" + base.hashCode());
        System.out.println("base.toString()  :" + base.toString());
    }
}
  実行結果
  base.equals(null):false
  base.hashCode()  :17510567
  base.toString()  :sample12.Base@10b30a7


オーバーライドでクラスの振る舞いを変える

 もし、クラスの振る舞いを変えたいのであれば、スーパークラスのメソッドを「オーバーライド(override、無視する)」するために、同名のメソッドを宣言し、あらためて実装します。単に拡張しただけでも、メソッドは“継承”のみされるのですが、このように明示的に実装をすることによって、プログラムの動作を変更できるのです。

オーバーライドにはアノテーションが必要?

 オーバーライドをする場合は、アノテーションの「@Override」を書いて、次の行にオーバーライドしたいメソッドの宣言と新しい処理を記述します。実は、アノテーションについては書かなくてもコンパイラではエラーが出ません。しかし、単純ミスを防ぐためにはこれを記述する癖を付けておいた方がいいですし、後ほど説明するEclipseの機能でメソッドのオーバーライドをすると、これが自動で付きますから覚えておくようにしてください。アノテーションについては、後の連載で別途説明しますので、ここでは単純に「@Override」と書いておけばいいと覚えれば十分です。

  @Override
  戻り型 メソッド名(仮パラメータ型 仮パラメータ名) {
      新しい処理
  }

Eclipseでクラスを拡張する

 それでは、sample12.Aクラスを拡張するsample12.Bクラスを用意し、aMethodメソッドのオーバーライドをしてみましょう。全部自分で記述してもいいですが、Eclipseにはメソッドのオーバーライドを簡単にするための機能が付いているので、それを使ってみましょう。

  1. パッケージエクスプローラでsample12パッケージをマウスの右ボタンでクリック
  2. 表示されるメニューで[新規]→[クラス]を指定
  3. [新規Javaクラス]の画面で[名前]に「sample12.B」を指定
  4. [新規Javaクラス]の画面で[スーパークラス]に「sample12.A」を指定して[完了]をクリック
  5. パッケージエクスプローラでsample12パッケージのB.javaをマウスの右ボタンでクリック
  6. 表示されるメニューで[ソース]→[メソッドのオーバーライド/実装(V)...]を指定
  7. AクラスのaMethodにチェック(図5
  8. [OK]をクリック
図5 オーバーライドするメソッドの指定
図5 オーバーライドするメソッドの指定

  次ページでは、さらにオーバーライドについて説明し、キーワード「super」でスーパークラスへアクセスする方法にも触れます。

1-2-3-4

 Index
第12回 継承やオーバーライドで簡単にクラスを“拡張”しよう
  Page1
上手にクラスの拡張を使ってプログラムを再利用しよう
「クラスを継承・拡張(extends)する」ってどういうこと?
コラム 「UMLでよく見る汎化(generalization)関係とは」
コラム 「extendsはJava開発現場の常識」
  Page2
Java標準の拡張されたクラスを使ってみよう
Page3
自作クラスを継承・拡張してみよう
コラム 「次回で詳しく解説する“アクセス制限”とは」
注釈 「『論理否定』って何か分かってる?」
コラム 「実は、あなたも継承しているObjectクラス」
オーバーライドでクラスの振る舞いを変える
  Page4
コラム 「なぜJavaは実装の多重継承ができないのか」
キーワード「super」でスーパークラスへアクセス!
コラム 「等値と同一」
継承や拡張は、オブジェクト指向の“肝”

【改訂版】Eclipseではじめるプログラミング バックナンバー 連載インデックスへ»



Java Solution全記事一覧

TechTargetジャパン

Java Solution フォーラム 新着記事

@ITメールマガジン 新着情報やスタッフのコラムがメールで届きます(無料)

RSSフィード

キャリアアップ

- PR -
@IT Sepcial

イベントカレンダー

PickUpイベント

- PR -
もっと見る
- PR -

お勧め求人情報

ホワイトペーパーTechTargetジャパン

@IT Sepcial
ソリューションFLASH