[Java]オーバーライドの利点
解決済
回答 5
投稿
- 評価
- クリップ 3
- VIEW 11K+
Javaのオーバーライドについて質問です。
オーバーライドの利点は、
継承により効率的にスーパークラスの機能を流用しつつ、
必要に応じてサブクラスでその機能を変更することができることだと
Web、書籍等には書いてありますが、これは実行クラスがスーパークラスとサブクラスの機能を順番に呼び出した場合と何が違うのでしょうか。
単純に、実行クラスのコーディングの可読性に優れるという理解でいいのでしょうか。
とんちんかんな質問をしてしまっているかもしれませんが
よろしくお願いいたします。
-
気になる質問をクリップする
クリップした質問は、後からいつでもマイページで確認できます。
またクリップした質問に回答があった際、通知やメールを受け取ることができます。
クリップを取り消します
-
良い質問の評価を上げる
以下のような質問は評価を上げましょう
- 質問内容が明確
- 自分も答えを知りたい
- 質問者以外のユーザにも役立つ
評価が高い質問は、TOPページの「注目」タブのフィードに表示されやすくなります。
質問の評価を上げたことを取り消します
-
評価を下げられる数の上限に達しました
評価を下げることができません
- 1日5回まで評価を下げられます
- 1日に1ユーザに対して2回まで評価を下げられます
質問の評価を下げる
teratailでは下記のような質問を「具体的に困っていることがない質問」、「サイトポリシーに違反する質問」と定義し、推奨していません。
- プログラミングに関係のない質問
- やってほしいことだけを記載した丸投げの質問
- 問題・課題が含まれていない質問
- 意図的に内容が抹消された質問
- 過去に投稿した質問と同じ内容の質問
- 広告と受け取られるような投稿
評価が下がると、TOPページの「アクティブ」「注目」タブのフィードに表示されにくくなります。
質問の評価を下げたことを取り消します
この機能は開放されていません
評価を下げる条件を満たしてません
質問の評価を下げる機能の利用条件
この機能を利用するためには、以下の事項を行う必要があります。
- 質問回答など一定の行動
-
メールアドレスの認証
メールアドレスの認証
-
質問評価に関するヘルプページの閲覧
質問評価に関するヘルプページの閲覧
checkベストアンサー
+2
おそらく質問者さんがわからなくなっていらっしゃることは、javaのオブジェクト指向という思想の部分ですね。設計思想というか、コンセプトというか、考え方というか、定石というか。
ポリモーフィズムとカプセル化について学ばれるとよいと思います。
オブジェクト指向においては、「変更に対して開かれており、修正に対しては閉じている」状態を目指すのがよいといわれています。要するに機能の追加や変更は容易でいくらでも付け足すことができ、逆に不具合などによって修正を行う部分は極小(可能な限り1箇所にとどまる)であるべきだ、という考えですね。
そういった思想的背景から、スーパークラスとサブクラスに分けるような機構が考えだされて、処理分割を行うようになりました。(歴史的経緯が正しいかどうかはおいておいて、そうだと思ってみてください。嘘も方便のつもりで書いています。)
よくある感じの説明をすると、人類クラスをスーパーにして、Aさん、Bさんサブクラスを作る場合、人類ができること(メソッド、話すとか歩くとか)の一般的な部分を人類クラスに記述し、AさんBさん固有の動作をAさんBさんクラスにオーバーライドして記述しておけばAさんの動作を変えたい場合にAさんクラスのみを修正すればよく、人類全体に影響が及ばない、ということになります。
また、クラスを使用する側の処理から見ると、より汎用的な型(Abstractやsuper,Interface)に対してプログラミングをしておけば、サブクラスの細かい仕様変更に際して修正を施す必要がなくなります。
同じ処理をいかに1箇所にとどめるか、いかに変更や修正に際して少ない箇所の修正で済むように作るか、という観点で見ると、わかりやすいのではないでしょうか。
投稿
-
回答の評価を上げる
以下のような回答は評価を上げましょう
- 正しい回答
- わかりやすい回答
- ためになる回答
評価が高い回答ほどページの上位に表示されます。
-
回答の評価を下げる
下記のような回答は推奨されていません。
- 間違っている回答
- 質問の回答になっていない投稿
- スパムや攻撃的な表現を用いた投稿
評価を下げる際はその理由を明確に伝え、適切な回答に修正してもらいましょう。
+1
>オーバーライドの利点は、
>継承により効率的にスーパークラスの機能を流用しつつ、
>必要に応じてサブクラスでその機能を変更することができること
についてはサブクラスのみ生成した前提の説明になります。
オーバライドはスーパークラスのメソッド上書きとなるので、サブクラスにおいてスーパークラスのメソッドは封じられます。ですが、サブクラスのメソッドでsuper識別子を使えばスーパークラスのメソッドを呼び出せるのです。
class car{
public void accel(){
System.out.println("エンジン駆動");
}
}
class hibrydCar extends car{
public void accel(){
super.accel();
System.out.println("モーター駆動");
}
}
オーバライドしたメソッド内でスーパークラスのメソッドを呼びコードを加えることが「効率的にスーパークラスの機能を流用しつつ、必要に応じてサブクラスでその機能を変更することができる」に掛かっていると思います。
参考サイト
スーパークラスとサブクラスを生成してそれぞれでメソッドを呼べば、同じ結果となるケースもありますがそれはまた別の議論になるかと思います。(勘違いでしたら済みません)
投稿
-
回答の評価を上げる
以下のような回答は評価を上げましょう
- 正しい回答
- わかりやすい回答
- ためになる回答
評価が高い回答ほどページの上位に表示されます。
-
回答の評価を下げる
下記のような回答は推奨されていません。
- 間違っている回答
- 質問の回答になっていない投稿
- スパムや攻撃的な表現を用いた投稿
評価を下げる際はその理由を明確に伝え、適切な回答に修正してもらいましょう。
+1
スーパークラスやスーパークラスを利用するクラス(サブクラスではなく、
java.util.List<?>
など)は、サブクラスの実装に影響されることなく、リリース後の機能強化などが可能です。
つまり、スーパークラスを主体とした利用時にメリットがあるという理解です。
例えば
JDBC
は、Javaのリリース当時に現在のMongoDB
などの出現は当然予想していませんが、今現在動いているのはこの恩恵です。
コネクションプーリングなどの機能強化も、まず
JDBC
側にメソッドを追加しリリースします。その後サブクラス(DBベンダー)がそのメソッドを実装したものをリリースすれば、利用者側でコネクションプーリングが使えるようになります。
一方、そもそもコネクションプーリング機能がないDBはいつまでも実装しないわけです。
投稿
-
回答の評価を上げる
以下のような回答は評価を上げましょう
- 正しい回答
- わかりやすい回答
- ためになる回答
評価が高い回答ほどページの上位に表示されます。
-
回答の評価を下げる
下記のような回答は推奨されていません。
- 間違っている回答
- 質問の回答になっていない投稿
- スパムや攻撃的な表現を用いた投稿
評価を下げる際はその理由を明確に伝え、適切な回答に修正してもらいましょう。
0
オーバーライドしないとうまくいかず、上記のような実行クラス側での呼び出しができない例を挙げてみました。
abstract class DataWriter { // いろいろなデータを出力するベースクラス
abstract void writeByte(int byte);
void writeInt(int i) { ... } // writeByteを用いて実装されている
void writeDouble(double d) { ... } // writeByteを用いて実装されている
... // 他にもwriteByteを用いて実装されている便利メソッドがたくさんある
}
class StreamDataWriter { // ストリームへデータを出力するクラス
OutputStream os;
StreamDataWriter(OutputStream os) { this.os = os; }
@Override void writeByte(int byte) { os.write(byte); }
// 他のメソッドはスーパークラスの実装のままでよいのでこれ以上は何も定義しなくてよい
}
class BufferDataWriter { // バッファへデータを出力するクラス
DataBuffer<Byte> buffer;
BufferDataWriter(DataBuffer<Byte> buffer) { this.buffer = buffer; }
@Override void writeByte(int byte) { buffer.write(byte); }
// 他のメソッドはスーパークラスの実装のままでよいのでこれ以上は何も定義しなくてよい
}
単なる例のためいまいちなクラスとなっていますが・・・
サブクラスであるStreamDataWriterとBufferDataWriterは出力する先が異なるだけです。お分かりと思いますがスーパークラスはバイトを出力するという機能だけをサブクラスがオーバーライドしてくれれば他のメソッドは自動的にうまく動くように意図して定義されています。
さて、このようなサブクラスに対して「実行クラスがスーパークラスとサブクラスの機能を順番に呼び出す...」というような方法ではwriteByte以外のメソッド呼び出しを記述しようがありません。説明が下手なのでみなさんのような一般性のある説明のしかたができないのですが、こんな例でも参考になるかもと思い記してみました。
いかがでしょうか。
投稿
-
回答の評価を上げる
以下のような回答は評価を上げましょう
- 正しい回答
- わかりやすい回答
- ためになる回答
評価が高い回答ほどページの上位に表示されます。
-
回答の評価を下げる
下記のような回答は推奨されていません。
- 間違っている回答
- 質問の回答になっていない投稿
- スパムや攻撃的な表現を用いた投稿
評価を下げる際はその理由を明確に伝え、適切な回答に修正してもらいましょう。
0
これは実行クラスがスーパークラスとサブクラスの機能を順番に呼び出した場合と何が違うのでしょうか。そもそも,これらを「順番に」呼び出すとは限りません.場合によっては,内部的に全く違う処理を行うことがあります.
例えば,Javaの原始クラスjava.lang.Objectクラスには
toString()
メソッドや equals(Object)
メソッドが有ります.
前者はオブジェクトの文字列表現を表し,後者は引数に取ったオブジェクトと比較するメソッドです.
そのまま使うと,
toString()
は「クラス名@ハッシュコード」という文字列を返し,
equals(Object)
は引数が自分自身(と同じ参照位置)である場合のみtrue,それ以外はfalseを返します.
そして,これらのメソッドは他のメソッドで使われます.
System.out.println(Object)
では引数となったオブジェクトのtoString()
が呼ばれ,その結果を出力します.equals(Object)
はListなどで,対象のオブジェクトと同じものがListに入っているかの判定などに使われます.
この時,元の仕様そのままでは都合が悪いことがあるでしょう.例えばオブジェクトの中身を表示したいのにハッシュコードが表示されたところで意味がありませんし,オブジェクトの中身の比較をしたいのに自分自身としか等しくならない
equals(Object)
なんて役に立ちません.
そこで,オーバーライドの出番です.
toString()
でオブジェクトの中身を文字列化したものを返したり,
equals(Object)
では中身が完全一致した場合にtrueを返したりという機能を実装したりします.
オーバーライドしたクラスのオブジェクトを
System.out.println(Object)
に渡せば,オブジェクトの中身を文字として出力できますし,比較もオブジェクトの中身を比較するのでより直感的になります.
こうすることで,スーパークラスであるObjectからの操作記述はそのままに,サブクラスである独自のクラスで独自の操作ができるようになります.
このように,スーパークラスのメソッドを使う場面で,サブクラスでメソッドの操作を変更できるのです.
もしオーバーライドしないのなら,自作クラスで何らかの文字列を返すメソッド(仮に
getString()
とします)を作り,System.out.println(myObject.getString())
と書かなければいけなくなったり,
List内に目的の中身を持つオブジェクトが入っているか調べる際もListのメソッドは使えず,
自分でループ処理を書いてオブジェクトの中身を調べるコードを書かなければならないでしょう.
投稿
-
回答の評価を上げる
以下のような回答は評価を上げましょう
- 正しい回答
- わかりやすい回答
- ためになる回答
評価が高い回答ほどページの上位に表示されます。
-
回答の評価を下げる
下記のような回答は推奨されていません。
- 間違っている回答
- 質問の回答になっていない投稿
- スパムや攻撃的な表現を用いた投稿
評価を下げる際はその理由を明確に伝え、適切な回答に修正してもらいましょう。
15分調べてもわからないことは、teratailで質問しよう!
- ただいまの回答率 88.22%
- 質問をまとめることで、思考を整理して素早く解決
- テンプレート機能で、簡単に質問をまとめられる
2014/12/08 10:50
修正が発生したとき、影響範囲を小さくできるというメリットがあるということですね。
しかしこれだけでは
>実行クラスがスーパークラスとサブクラスの機能を順番に呼び出した場合
と同じなように思えるのですが、これはオーバーライドがカプセル化の設計思想にも即している思想なので推奨されているという理解でよいでしょうか。
それともう一つ、
>AさんBさん固有の動作をAさんBさんクラスにオーバーライドして記述しておけばAさんの動作を変えたい場合にAさんクラスのみを修正すればよく、人類全体に影響が及ばない、ということになります。
挙げていただいた例ですが、逆に人類クラスに変更があった場合はAさん、Bさん...すべてに影響があるということになりますよね。
たいていは親が変わるのだから子も変わってほしい場合が多いと思うのですが、親の影響を受けるのが問題だと思われる場合は、委譲で実装することになるのでしょうか。
2014/12/13 17:27
>>実行クラスがスーパークラスとサブクラスの機能を順番に呼び出した場合
>と同じなように思えるのですが、これはオーバーライドがカプセル化の設計思想にも即し>ている思想なので推奨されているという理解でよいでしょうか。
サブクラスの機能を呼び出す際のインターフェース変更はこの限りではありませんよね。
リファクタリングによる名称変更や引数の変更などを想定しています。
>挙げていただいた例ですが、逆に人類クラスに変更があった場合はAさん、Bさん...す>べてに影響があるということになりますよね。
>たいていは親が変わるのだから子も変わってほしい場合が多いと思うのですが、親の影>響を受けるのが問題だと思われる場合は、委譲で実装することになるのでしょうか。
うまく処理分割を実施していれば、変更の内容や規模にもよりますが、基本的には親だけの変更で済みます。
処理分割があまりよく考えられていない場合、言い換えると親クラスと子クラスの結合度が高い実装の場合、親クラスを変更するために子クラスを変更しなくてはならないとか、逆に子クラスを変更するために親クラスを変更しなければならないとかいった事態に陥りますが、それは設計に失敗しています。
それから、おっしゃるように継承より委譲、ですね。その方がうまくいくことが多いなあと感じます。本当ならひとつの存在(同じドメイン)の存在を無理に親子に分けてしまって、常に2つセットで修正しないといけない、というような何をやっているのかよくわからない事態は避けるべきです。
どうクラスを分けるか、がほとんどすべてだと思います。