
継承やオーバーライドで簡単にクラスを“拡張”しよう
株式会社ガリレオ
小山博史
2009/11/13
■ オーバーライドするサンプル
出来上がったクラスを、次のように編集します。ここではスペースの都合上から、コメントや無駄な行を削除しています。sample12.Aクラスでは、「"aMethod:baseField:" + baseField」を出力して、「!baseMethod()」の計算結果(true)を返すという処理でした。sample12.Bクラスでは、代わりに「"オーバーライド済 :"」と出力してから、必ず「false」を返すというメソッドに変更しています。
sample12/B.javaclass B extends A {
@Override
boolean aMethod() {
System.out.println("オーバーライド済 :");
return false;
}
}
次に、これを利用するsample12.Sample03クラスを作成します。起動メソッド(mainメソッド)を持ち、その中でsample12.Bクラスのインスタンスを生成し、それが持つフィールドにアクセスしたり、メソッドを呼び出したりしています。比較しやすくするために、同様の処理をsample12.Aクラスについても行っています。
sample12/Sample03.javapackage 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キーワードを使います。今回は詳細を説明しませんが、構文自体は覚えておくといいでしょう。インターフェイスは多重継承をサポートしていますから、複数のインターフェイスを拡張できます。 構文
例では、java.lang.Runnableインターフェイスを拡張しているので、RunnablePointは宣言されているメソッド以外に、java.lang.Runnableインターフェイスで宣言されているrunメソッドについてインターフェイス継承しているということになります。ですから、RunnablePointインターフェイスを実装するクラスは、getXメソッドなどの4つのメソッドだけでなく、runメソッドも実装する必要があります。 |
キーワード「super」でスーパークラスへアクセス!
キーワード「super」を使うことにより、スーパークラスへアクセスできます。このキーワードは、クラスのすべてのstaticではないメソッドの中で利用できます。thisキーワードを使うと、自分自身のインスタンスを参照できましたが、このキーワードは自分自身をスーパークラスのインスタンスと見なして、スーパークラスのフィールドやメソッドへアクセスできます。
sample12/C.javapackage 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.javapackage 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コレクションフレームワーク」(ソフトバンククリエイティブ)、そのほかに雑誌執筆多数。 |
| Index | ||||||||||
|
||||||||||
【改訂版】Eclipseではじめるプログラミング バックナンバー 連載インデックスへ»
- 第1回 Eclipse 3.4で超簡単Javaプログラミング基礎入門
- 第2回 Javaで一から理解するプログラムの変数と演算子
- 第3回 プログラミングの醍醐味! Javaで“条件式”を理解する
- 第4回 プログラミングの真骨頂! Javaで“反復処理”を覚える
- 第5回 データ集合を扱うのに便利なJavaの配列と拡張for文
- 第6回 複雑なデータを表現できるクラスやフィールドって?
- 第7回 クラスの振る舞いを表すJavaの“メソッド”とは?
- 第8回 Javaの参照型を文字列操作で理解して文法を総復習
- 第9回 プログラムを「変更」しやすくする“インターフェイス”
- 第10回 Javaの実案件に必須のパッケージとインポートを知る
- 第11回 「static」でクラス共有の変数・メソッドを使いこなせ!
- 第12回 継承やオーバーライドで簡単にクラスを“拡張”しよう
- 第13回 “コンストラクタ”と初期化、本当に理解できてる?
- 第14回 再利用性の高いクラス作成に重要な“アクセス制御”
- 第15回 Javaは「抽象クラス」で実装を上手に再利用できる
- 第16回 “ネスト”した型で始める軽量Javaプログラミング!?
- 第17回 あなたの知らない、4つのマニアックなJava文法
- 第18回 強く型付けされているJavaの理解に必修の“型変換”
- 第19回 キュー構造をJavaで実装してジェネリック型を理解する
- 第20回 拡張for文の真の実力を知り、反復処理を使いこなせ
- 第21回 7ステップで理解するJavaでの列挙型/enum使用法
- 第22回 いまさら聞けない「Javadoc」と「アノテーション」入門
- 第23回 プログラマの宿命! 例外とエラー処理を理解する
- 第24回 Javaの例外処理で知らないと損する7つのテクニック
| Java Solution全記事一覧 |
TechTargetジャパン
- Scalaのパッケージ、アクセス修飾子、オブジェクト継承 (2012/5/22)
インポート、パッケージオブジェクト、抽象クラス/抽象メソッド、オーバーライド、final、シールドクラスなども - 基幹系システムでCloud SQLは使えるか試してみた (2012/5/17)
サンプルとしてMRPシステムを作成して動かし、「再帰呼び出し」などのパフォーマンスを測定して検証してみます - アジャイル管理ツール9選+Pivotal Tracker入門 (2012/5/14)
群雄割拠のアジャイルプロジェクト管理ツールを9つ紹介し、特に注目を集めているPivotal Trackerの基本的な使い方を解説します - サーバサイドJSやJavaでWebアプリが作れるXPages (2012/5/11)
Notes/Dominoの資産をサーバサイドJavaScriptやJavaで操作し、HTMLやJavaScript、CSSをUIにできる技術を紹介
|
|
キャリアアップ
スポンサーからのお知らせ
- - PR -
イベントカレンダー
- - PR -
