【オブジェクト指向入門】継承とは?(基本編)わかりやすく解説

バトンリレー

こんにちは、ハニ太郎(@82_taro)です♪

プログラムを作っていると、以前作ったクラスと似通ったクラスを作る必要に迫られ、面倒に感じることも増えてきます。

今回の記事では、そんな課題を解決してくれるオブジェクト指向の花形機能である「継承」についてわかりやすく解説していきたいと思います。

この記事を読み終えた頃には「継承とは何か?」きっと理解できていることでしょう。

ハニ太郎

最後まで読んでね♪

はじめに

継承について理解する上で、オブジェクト指向の基礎知識は必要不可欠です。

それらの知識について不安のある方は先に以下の記事を読んでおいてください。

>【初心者向け】オブジェクト指向とは?世界一わかりやすく解説してみた

上記の記事では、オブジェクト指向の定義やメリットについて解説しております。

継承の意義

プログラムを作っていると以前作ったクラスと似通ったクラスを作る必要に迫られることがあります。

”ほとんど同じだけどメソッドが一つだけ多い”
”ほとんど同じだけどフィールドが2つほど多い”

といった具合です。

では、そのようなクラスはどのようにすれば効率よく作ることができるでしょうか?

コピー&ペーストで解決?

真っ先に思い浮かぶのが、コピペで解決する方法。

実際にコピペを行い、随時必要なフィールドやメソッドを追加することでも一時的には解決できます。

エラーも出ません。

「ならコピペでよくね?」と思うかもしれませんが、コピペによって作られたクラスには次のような2つの問題があるのです。

  1. 追加・修正に手間がかかる
  2. 把握・管理が難しくなる

それぞれの問題点についてみていきましょう。

追加・修正に手間がかかる

さて、コピペの元となったクラスに変更が生じた場合のことを考えてみましょう。

コピペで作ったクラスでも同じ変更をする必要があるのは容易に想像できるかと思います。

クラスの数が少なければ少しの手間で済みますが、数十数百のクラスの内容を書き換えなければいけないとなるとかなり面倒だとは思いませんか?

このようにコピペで問題を解決しようとするとのちに厄介な問題が発生してしまうのです。

把握・管理が難しくなる

コピペで問題を解決する場合、ソースコードの大半が重複してしまうことになります。

これによりプログラム全体の見通しが悪くなり、メンテナンスがしずらくなってしまうのです。

継承による解決

これらの問題点を解決してくれる機能が「継承

「継承」では、各言語に用意された文法を用いることで、元となるクラスとの「差分」だけを記述して新たなクラスを宣言することができるのです。

新たに定義するクラスに着目すると「元となるクラスからメンバが引き継がれているように見える」ことから「継承」という名前がついています。

継承とは
コードの重複記述を減らすための道具。

元のクラスとの差分のみ記述することから元のクラスに変更があっても書き換える必要はありませんし、コードが重複することもありません。

ゆえに「継承」を用いることによって、上記2点の問題(追加修正に手間がかかる、把握管理が難しくなる)を解消したうえで、効率的に似通ったクラスを作成することができるのです。

継承関係

継承を行う際そのクラス同士の関係を「継承関係」と呼びます。

また、元となるクラスを「親クラス」、新たに定義されるクラスを「子クラス」などと呼びます。

なお、図で表したものが以下。

継承関係

親から子が生まれるのだから矢印の向きが違うのでは?」と思われたかもしれませんが、この向きで正しいのです。

継承関係図において、矢印を直感とは逆向きで描くのには理由があります。

その理由は、次章の「汎化・特化について」で説明いたしますのでしばしお待ちくださいませ。

継承の本質とは?

ここまでで継承の基礎中の基礎については理解できたかと思います。

各言語に用意された特殊な文法を用いてクラスを宣言することで、元となるクラスとの「差分」だけを記述して新たなクラスを宣言することができるのでした。

だとすると「似通ったクラスは全て継承して作ってしまえば良いのではないか?」と思うかもしれませんが、決してそうではありません。

継承には「正しい継承」と「間違った継承」が。

読者の皆様には、継承の本質を理解してもらい正しい継承のみを利用していただきたいと思います。

正しい継承とは?

正しい継承とは、ズバリ「is-aの原則」に則っている継承のことです。

is-aの原則
子クラスis-a親クラス(子クラスは親クラスの一種である

この文章に当てはめた際、意味が自然であれば正しい継承と言えます。

逆に「子クラスis-a親クラス(子クラスは親クラスの一種である)」という文章に違和感を感じたら、継承の誤りを疑いましょう。

そのため、現時点で似たような機能を持っているからといってむやみに継承を使ってはいけません。

「動くか動かないか、便利か便利でないか」ではなく、is-aであるかどうかに基づいて継承は利用すべきなのです。

継承を使う判断基準
is-aの原則が成立しないならば、継承を使ってはならない。

間違った継承をすべきでない理由

is-aの関係ではない継承を使ってはならない理由は大きく下記の2点となります。

  1. 将来、クラスを拡張していった場合に現実世界との矛盾が生じるから。
  2. 「多態性」を利用できなくなるから。

それぞれについて詳しくみていきましょう。

将来、クラスを拡張していった場合に現実世界との矛盾が生じるから

例えば「Itemクラス」と「Carクラス」が存在するとします。

*Itemクラスにはname属性とPrice属性だけがが存在。今回は戦闘ゲームを想定。

この状態だとCarクラスでも名前と価格は必要と考えられるため継承しても問題ないように思えます。

しかし、のちにItemクラスでgetDamage()という相手に投げつけてダメージを与えるメソッドを追加したらどうなるでしょうか?

さすがに車を投げつけるというのは現実的ではありませんよね?

現実との矛盾が生じてしまいます。

このように現時点で継承した時に実害はなくとも間違った継承をしてしまうとのちにおかしな動作をしてしまうということが起こり得てしまうのです。

「多態性」を利用できなくなるから

多態性については「そういうものがあるんだな」と現時点では思っていただいて結構です。

なお、多態性については以下の記事で解説しているので継承について理解できたら読んでみてください。

>【オブジェクト指向入門】多態性(ポリモーフィズム)とは?わかりやすく解説

汎化・特化について

正しい継承が「is-a」の関係で結ばれるということは、子クラスになるほど「特殊で具体的なもの」に具体化(特化)していき、親クラスになるほど「一般的で、抽象的なもの」に一般化(汎化)していくことになります。

ここで継承関係の図を思い出してみましょう。

前章の継承関係の図の中の矢印は「クラスが汎化していく方向」を表すための矢印なので、直感とは逆向きとなっていたのです。

ところで、これまで継承を「コードの重複記述を減らすための道具」と捉えていたと思います。

しかし、継承は「ある2つのクラスに特化・汎化の関係があることを示すための道具」でもあるということがここで理解できたのではないでしょうか?

継承とは
ある2つのクラスに特化・汎化の関係があることを示すための道具。

これが継承の本質になります。

おわりに

ハニ太郎

ここまで記事を読んでいただきありがとうございました♪

あなたが本記事で継承についての理解を深めることができていたら幸いです(^^)

なお、本記事は継承の基礎となります。

>【オブジェクト指向入門】継承とは?(応用編)わかりやすく解説

本記事の内容が理解できたら上記の記事にチャレンジしてみましょう。

これからの学習も大変かとは思いますが頑張ってください!

コメントを残す