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

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


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


オーバーライドするサンプル

 出来上がったクラスを、次のように編集します。ここではスペースの都合上から、コメントや無駄な行を削除しています。sample12.Aクラスでは、「"aMethod:baseField:" + baseField」を出力して、「!baseMethod()」の計算結果(true)を返すという処理でした。sample12.Bクラスでは、代わりに「"オーバーライド済 :"」と出力してから、必ず「false」を返すというメソッドに変更しています。

  sample12/B.java
class B extends A {
    @Override
    boolean aMethod() {
        System.out.println("オーバーライド済 :");
        return false;
    }
}

 次に、これを利用するsample12.Sample03クラスを作成します。起動メソッド(mainメソッド)を持ち、その中でsample12.Bクラスのインスタンスを生成し、それが持つフィールドにアクセスしたり、メソッドを呼び出したりしています。比較しやすくするために、同様の処理をsample12.Aクラスについても行っています。

  sample12/Sample03.java
package sample12;
public class Sample03 {
    public static void main(String[] args) {
        B b = new B();
        // BクラスはAクラスからフィールドとメソッドを継承
        System.out.println("b.baseField      :" + b.baseField);
        System.out.println("b.baseMethod()   :" + b.baseMethod());
        // BクラスはAクラスのaMethodメソッドをオーバーライド
        System.out.println("b.aMethod()      :" + b.aMethod());
        System.out.println("-------------------------");
 
        // Aクラスの場合
        A a = new A();
        System.out.println("a.baseField      :" + a.baseField);
        System.out.println("a.baseMethod()   :" + a.baseMethod());
        System.out.println("a.aMethod()      :" + a.aMethod());
    }
}

 実行結果は、次のようになります。結果から、sample12.Bクラスは、sample12.Aクラスのフィールドやメソッドを継承していることが分かります。また、aMethodメソッドの処理結果を見ることにより、sample12.Bクラスとsample12.Aクラスとは振る舞いが変わっていて、確かにsample12.Bクラスがsample12.AクラスのaMethodメソッドをオーバーライドしていることが分かります。

  sample12.Sample03クラスの実行結果
b.baseField      :1
b.baseMethod()   :false
オーバーライド済 :
b.aMethod()      :false
-------------------------
a.baseField      :1
a.baseMethod()   :false
aMethod:baseField:1
a.aMethod()      :true

コラム 「なぜJavaは実装の多重継承ができないのか」

Javaでは、「単一継承モデル」といわれるモデルを採用しているため、クラスの拡張をする場合に、スーパークラスとして指定できるのは1つのクラスだけです。つまり、1つのクラスを拡張して新しいクラスを作れますが、2つ以上のクラスを組み合わせて拡張して新しいクラスを作れません。

2つ以上のクラスを組み合わせて拡張することは、「実装の多重継承」といわれますが、Javaではこれを許していません。しかし、多重継承の恩恵を得るために、Javaでは「インターフェイスの多重継承」はできます。実装の多重継承が有用な場面もありますが、問題となる実装がされてしまうこともあります。Javaはシンプルな実装ができるように、多重継承については限定する方向で言語設計されたのです。

ちなみにインターフェイスの拡張をするためにも、extendsキーワードを使います。今回は詳細を説明しませんが、構文自体は覚えておくといいでしょう。インターフェイスは多重継承をサポートしていますから、複数のインターフェイスを拡張できます。

  構文
  interface インターフェイス名 extends スーパーインターフェイス名 {
      メソッドの宣言
  }
  例
  interface RunnablePoint extends java.lang.Runnable {
      int getX();
      int getY();
      void setX(int x);
      void setY(int y);
  }

例では、java.lang.Runnableインターフェイスを拡張しているので、RunnablePointは宣言されているメソッド以外に、java.lang.Runnableインターフェイスで宣言されているrunメソッドについてインターフェイス継承しているということになります。ですから、RunnablePointインターフェイスを実装するクラスは、getXメソッドなどの4つのメソッドだけでなく、runメソッドも実装する必要があります。

キーワード「super」でスーパークラスへアクセス!

 キーワード「super」を使うことにより、スーパークラスへアクセスできます。このキーワードは、クラスのすべてのstaticではないメソッドの中で利用できます。thisキーワードを使うと、自分自身のインスタンスを参照できましたが、このキーワードは自分自身をスーパークラスのインスタンスと見なして、スーパークラスのフィールドやメソッドへアクセスできます。

  sample12/C.java
package sample12;
class C extends A {
    @Override
    boolean aMethod() {
        // スーパークラスのaMethodメソッドを呼び出す
        boolean b = super.aMethod();
        System.out.println("super.aMethod()  :" + b);
        System.out.println("オーバーライド済 :");
        return false;
    }
}

 次のようなクラスを用意することにより、sample12.Cクラスの動作を確認できます。

  sample12/Sample04.java
package sample12;
public class Sample04 {
    public static void main(String[] args) {
        C c = new C();
        System.out.println("c.aMethod()      :" + c.aMethod());
    }
}

 実行結果は、次のとおりです。最初の行は、スーパークラスのaMethodメソッドにより出力されています。2行目の結果を見て分かるように、スーパークラスのaMethodメソッドの計算結果のtrueが変数bに代入されています。3行目はsample12.CクラスのaMethodメソッド内の処理で、sample12.CクラスのaMethodメソッドの計算結果は4行目よりfalseだったことが分かります。どうでしょう、思ったとおりの結果になりました。

  sample12.Sample04クラスの実行結果
aMethod:baseField:1
super.aMethod()  :true
オーバーライド済 :
c.aMethod()      :false

コラム 「等値と同一」

クラスを拡張した際には、そのクラスのオブジェクトの等値性をどのように定義するのか、きちんと考える必要があります。Stringのように、「表現する文字列が同じものなら、インスタンスは別でも、equalsメソッドの結果はtrue」としたいクラスを作りたくなることもあります。その場合は、Objectクラスのequalsメソッドをオーバーライドする必要が生じてきます。

Objectクラスのequalsメソッドは、保持するデータが等値(Equality)であるかではなく、インスタンスが同一(Identity)であるかどうかを判定する処理となっているため、これをクラスに合わせて等値判定をする処理にします。

equalsメソッドをオーバーライドした場合は、hashCodeメソッドのオーバーライドも必要です。このメソッドはコレクションフレームワークのクラス(java.util.HashMapなど)で等値判定に利用されていることがあります。equalsメソッドにより等値判定が変わるのであれば、hashCodeメソッドもその等値判定に合わせた処理へ変更する必要があります。

ちょっと大変そうに聞こえたかもしれませんが、実は、これらを簡単に実装するためのクラスが、「Apache Commons Lang」にあります。これに含まれるorg.apache.commons.lang.builder.EqualsBuilderやorg.apache.commons.lang.builder.HashCodeBuilderです。実際に変更が必要な場面に出会ったら、これらの利用を検討してみるといいでしょう。

継承や拡張は、オブジェクト指向の“肝”

 以上で説明はお終いですが、クラスの拡張について理解できたでしょうか。ポリモーフィズムにより、「クラスBがクラスAを拡張している場合、クラスBは、クラスAとしての形態と、クラスB としての形態と両方を持つことができる」ということについて、java.lang.Objectとjava.lang.Stringを使って説明をしました。下記をポイントとして覚えておきましょう。

  • クラスBのインスタンスへの参照は、クラスA型の変数へ代入できる
  • クラスA型の変数が参照するインスタンスの型によって、メソッドの処理が変わる

 次に、クラスを拡張して、新しいクラスを作成する方法は理解できたでしょうか。「既存のクラスへ新しいフィールドや新しいメソッドを追加するには、extendsによるクラス拡張をすればいい」ということがポイントです。そして、「その際、既存クラスのフィールドやメソッドは継承される」という点もポイントとして覚えておきましょう。

 メソッドのオーバーライドをすることにより、拡張したクラスの方でスーパークラスとは違った振る舞いを持たせることができることも説明をしました。こういった定義をできることが、オブジェクト指向の“肝”でもありますから、よく理解をしておいてください。また、superを使うことにより、スーパークラスのフィールドへアクセスしたり、メソッドを呼び出しできることも説明しました。これらを理解することにより、思った通りの処理を無駄なコーディングを追加することなく実現できるようになります。

 今回の内容を理解することにより、クラスの拡張がどういったものか理解できたと思います。ただし、自分のクラスが拡張されて使われることまで考えてクラスの設計をしたり実装をしたりするには、このほかにも知っておくべき基礎知識があります。次回は、これまで詳しく説明をしてこなかったコンストラクタアクセス制御について説明する予定です。これらを理解することにより、拡張性や使い勝手も考慮に入れたクラス設計や実装がきちんとできるようになります。お楽しみに。 

 今回作ったサンプルのソースコードはこちらからダウンロードできます。

@IT関連記事


クラスの継承の本質を知る
[連載]いま
から始めるJava(4) クラスの継承を抽象的に理解しようとすると難しくなってしまいます。メモリの拡張として理解すれば実に簡単であることが分かります
Java Solution」フォーラム 2003/3/8
メソッドの挙動を変えるオーバーライド
[連載]いまから始めるJava(8) 前回作成したHTMLパーサのメソッドの機能をオーバーライドを使い変更します。オーバーライドは同名のメソッドに異なる機能をもたせることができます
Java Solution」フォーラム 2003/8/16
Java SE コアAPI 使用コード例一覧
Java開発者/プログラマのための、Java SEコアAPIの使用コード例の記事へのJavaDocっぽいリンク集。メソッドやコンストラクタ、例外などAPIの使い方の参考にしてください
Java Solution」フォーラム 2009/3/24
ActionScript 3なら継承&実装で大規模開発もできる
Flashの基礎を無料で習得! ActionScript入門(5) スクリプト言語でも大規模開発をしたい方必見! AS3は継承や実装などのオブジェクト指向開発ができるんです
リッチクライアント & 帳票」フ ォーラム 2008/5/13
Curlによるオブジェクト指向開発と強力な標準クラス
Curlで始めるリッチクライアント(2)
 オブジェクト指向でコンポーネントの再利用化を実現。さらにRecordSetクラスを使って、Excel相当のスプレッドシートを表現してみよう
リッチクライアント & 帳票」フ ォーラム 2005/6/17
コードをもっとオブジェクティブに
Cocoaの素、Objective-Cを知ろう(6)
 オブジェクト指向に基づいた体系的なクラス設計を学ぼう。コードの重複を排除し、効率的でカスタマイズ性の高いプログラムができる
Coding Edge」フォーラム 2009/3/6
継承を使うために知っておくべきこと
連載:オブジェクト指向プログラミング超入門(4) 
継承は難しい概念だが、実際のプログラミングで使う継承の機能は限られている。まずは、これだけをマスターしておこう
Insider.NET」フォーラム 2004/9/25
継承とは隠された条件判断である
連載:熱血VBプログラマ応援団(11) 
VB.NETでは「継承」が利用可能だが、難しそうで何に使えばよいのかピンとこない。そんなあなたに贈る「継承に取り組む1つの方法」
Insider.NET」フォーラム 2004/8/18
VB.NETにおける継承とポリモーフィズム
連載:改訂版 VB.NETプログラミング(10) 
VB.NETを使いこなすうえで最大の難所ともなるポリモーフィズム。サンプル・プログラムによる実例を示しながら、その真価を探る
Insider.NET」フォーラム 2004/6/24
第4章 C#における継承とインターフェイス
連載:改訂版 C#入門
 継承とインターフェイスは、OOP言語であるC#を学ぶうえで避けては通れない重要な基本機能である。これらはなぜ必要で、何の役に立つのか?
Insider.NET」フォーラム 2002/9/11

筆者プロフィール
小山博史(こやま ひろし)
情報家電、コンピュータと教育の研究に従事する傍ら、オープンソースソフトウェア、Java技術の普及のための活動を行っている。長野県の地域コミュニティである、SSS(G)bugs(J)の活動へも参加している。
著書に「基礎Java」(インプレス)、共著に「Javaコレクションフレームワーク」(ソフトバンククリエイティブ)、そのほかに雑誌執筆多数。

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