2019/02/06
継承って何なのか?よく分からない人の為に、その概念を解説します。
sponsored link
人間を継承したプログラマー
例えばこんなHumanクラスがあるとしましょう。
class Human{ String name; eat(){} sleep(){} }
このHumanクラスを継承して、Programmerクラスを作ります。
あるクラスを継承したクラスを作るには、クラス名の後ろに
extends クラス名
を付けるだけです。
class Programmer extends Human{ programming(){} }
これでHumanクラスを継承したProgrammerクラスが出来ました。
継承元であるHumanクラスのことを親クラス、スーパークラス、基底クラスなんていう風に呼びます。
Programmerクラスで定義されているのはご覧のようにprogramming()メソッドのみですが、プログラマーはプログラミングしかできないのではありません。Humanクラスを継承してるので、食べることも寝ることも出来るし、名前も付けられます。
もしこのProgrammerクラスを継承なしで書くなら、こういうことです。↓
class Programmer { String name; eat(){} sleep(){} programming(){} }
Humanクラスのメンバをコピペしてから、programming()メソッドを追加した状態です。
継承することで、継承元が持つメンバ(フィールド及びメソッド)を書かずとも有るものとして使えるわけです。親クラスが持つメンバに追加するメンバだけ書き足せばいいので楽ちんです。
継承を使って書こうと、親クラスからコピペでメンバを書こうと、出来あがったProgrammerクラスは全く同じ機能を持ってはいます。しかし、もしHumanクラスのメンバをコピペしてProgrammerクラスを書いてしまっていたら、当然ながらHumanクラスを書き変えてもProgrammerクラスには何の影響もありません。
なので、もしHumanクラスにwalk()メソッドもあった方がいいな。なんてことになって後から付け足した場合、Programmerクラスにも付け足さないといけないことになります。もしProgrammerクラスだけでなく、DoctorクラスやTeacherクラスなどの人間的なクラスをたくさん書いていたら、それら全部のクラスにwalk()メソッドを付け足す必要に迫られます。
コピペをするのではなく初めから継承を使っていたら、親クラスであるHumanクラスにwalk()メソッドを書き足すだけで済むので楽ですね。
継承の本質
とは言え、継承とは単にコピペするのが面倒だから使うものではありません。継承の本質はそこじゃありません。重要なのは
プログラマーは人間である
と言うことです。あ、人権うんぬんの話じゃないですよ。(笑)
これは継承の最も重要なポイントです。
class Child extends Super
とした場合、
Child is Super
の関係が成り立ちます。
プログラマー is 人間です。
人間クラスを継承することで、ある特殊な人間(プログラマー)を作るわけです。そしてどんな特殊な人間も人間であることには変わりはありません。人間クラスを継承してるクラスは全て人間です。
なので、ProgrammerクラスのインスタンスをHuman型オブジェクトとして扱うことが可能になります。
Human programmer = new Programmer();
プログラマーは人間として扱っていいんです。
プログラマーだけでなく人間クラスを継承しているクラスなら医者も先生もプログラム上、人間として扱えるようになります。
Human[] humans; humans = { new Programmer(), new Doctor(), new Teacher() };
Human配列の要素として、子クラスのインスタンスを格納しています。こういうことが出来るのは継承という機能のおかげです。
大事なことなのでもう一度言います。
Child is Super
これが継承の本質です。
継承の間違った使い方
逆に言えば、クラスを設計する際にChild is Superが成り立たないような継承はするべきではありません。
例えば、Dogクラスを書こうと思って、「bow()とeat()とwalk()とsleep()か・・、書くの面倒くせーな。。お、Humanクラスのメンバ継承しちゃえばいいじゃん♪ってことで、
class Dog extends Human{ bow(){} }
としてしまった場合、
Dog is Human
の関係が成り立つことになってしまいます。プログラム上、犬は人間の一種だということになってしまいます。おかしいですよね。
別に犬が人間であろうと動物であろうと植物であろうとそんなの関係ない。必要なメソッドを持ってるクラスを継承して何が悪いんだ。とは思わないで下さい。その時は何の問題もなかったとしても、もし後からHumanクラスにwork()メソッドを付けたすと、仕事をするDogオブジェクトが誕生してしまいます。
別にDogクラスにwork()メソッドがあろうとstudy()メソッドがあろうと、要らないメソッドは使わなかったらいいだけじゃないか。とは思わないで下さい。人間クラスは人間らしいクラスにし、犬クラスは犬らしいクラスにしないと、クラス設計が無茶苦茶になってきます。ちょっとぐらい大丈夫だと思って辻褄の合わないクラスの設計をし始めてしまうと、そのうち自分でもワケが分からなくなるのがオチです。このDogクラスって犬のことじゃないのか?なんてことになったら、もはやオブジェクト指向の意味がありません。他人様が読んだら発狂したくなるレベルです。
継承の正しい使い方
Child is Superというのは、言い方を変えると子クラス(Child)よりも親クラス(Super)の方が抽象度が高いということです。
例えばこんな継承関係があったとします。↓
プログラマー → 人間 → 動物 → 生物
ちなみに継承を矢印で表現する時は、このように子クラスから親クラスへの矢印で表現します。なんとなく親から子に矢印を指したくなる気持ちも分かりますが、反対ですのでご注意を。
親に行けば行くほど抽象的なモノになっているのが感覚的に分かると思います。これが正しい継承の仕方です。Child is Superの原則が崩れないように継承していけば、全体として辻褄の合ったクラス設計が出来るはずです。
犬 → 人間 → 動物 → 生物
↑これではおかしいというのが感覚的に分かりますよね。
犬クラスを書くとしたら、動物クラスを継承して書けば自然なクラス設計と言えます。
犬 → 動物 →生物
つまりこの場合、人間クラスと犬クラスが動物クラスを継承している状態にするのが正解と言えます。
全てのクラスの先祖 Objectクラス
JavaのAPIで用意されているクラスがいろいろありますね。例えばStringクラスだとかIntegerクラスだとか例を挙げるとキリがないですが、それら全てのクラスはjava.lang.Objectクラスを継承しています。言い方を変えるとjava.lang.Objectクラスがクラス階層のルートです。
なので全てのクラスはObjectクラスが持つメンバをひっそりと持っていることになります。equals()メソッドやtoString()メソッドは比較的よく使われると思いますが、これらは元々Objectクラスで定義されているメソッドです。
参考:Object (Java Platform SE 8 )
つまりはObjectクラスにて定義されているメソッドは、全てのクラスのインスタンスメソッドとして使用可能だと言うことです。
親クラスは1つだけ
Javaでは、同時に複数のクラスを継承することはできません。
蝶のように舞い、蜂のように刺すボクサーを作りたいからと言って、
class Boxer extends 蝶, 蜂{ punch(){} }
↑こんな書き方はできません。継承元として選べるのは1つだけです。
もし複数のクラスの機能を継承できたら、それはそれで便利だと思います。ですが、Javaではそれは出来ない仕様になっています。
それは、Objectクラスを頂点とし上に行けば行くほど抽象度が高いという理想的なクラス設計を実現する為なのかなと思います。
複数の親を持てるとなると、親元を遡れば遡るほとどんどん発散していってしまう構造になり、とても1つのクラス(Objectクラス)に収束するはずがないですから。
難しいことはさて置き、親クラスは1つだけというのは大事なルールなので覚えておいて下さい。
継承には細かくてややこしいルールもいろいろありますが、まずは概念だけでもしっかり理解しておきましょう。継承に関する細かいルールについては次回以降に。
今だけ→転職できなければ全額返金の「エンジニア転職保証コース」
絶対エンジニアになる!→テックエキスパート
フリーランスエンジニアの収入例を見てみる→レバテックフリーランス
コメント
サイト更新されたのですね!
相変わらず最高にわかりやすいです。ありがとうございます。
by 匿名 2015/07/07 20:51
ありがとうございます!
by Nobuo@管理人 2015/07/08 15:38
すごくわかりやすく説明して頂きたね。
本当にありがとうございました!
次回以降って、説明がまだありますよね?
読んでみたいです!
by ゆみ 2015/07/09 10:39
ありがとうございます。
また書こうと思っています。が、いつになるか未定です。(^^;)
by Nobuo@管理人 2015/07/10 13:23
はじめまして!アメリカのコミュカレでJava1を勉強したのですが、勉強している最中は分かっていたつもりが2に上がろうとした途端、何も身についていない事実に焦ってこちらのサイトに辿りつきました。ノブオさんの解説は日本語だから分かりやすいという以上に「痒いところに手が届く」もので、本当に分かりやすく、萎みかけていたプログラマーへの意欲も再び膨らんできました!! こちらの次回更新まで、Java1の復習して、秋期のJava2に備えたいと思います。丁寧に教えてくださって本当に有難うございます。更新楽しみにしてますね。
by みさ 2015/07/25 17:19
嬉しいコメントありがとうございます。癒されました。
お勉強頑張って下さい!
by Nobuo@管理人 2015/07/26 09:42
いつもわかりやすい解説ありがとうございます。
継承の本質のところで
Human[] humans;
humans = { new Programer(), new Doctor(), new Teacher() );
の記述で、new Teacher() }←これ、じゃあないのですか?
初心者なので質問の仕方もよくわかっていません、すんません。
by 匿名 2015/08/25 22:49
間違ってました。
ご指摘ありがとうございます。修正しておきました。
by Nobuo@管理人 2015/08/26 16:08
大学の実験で突然Javaのスキルが要求されたためこちらのサイトで勉強させていただいております。
今までCだけしか書いたことのない私にもとてもわかりやすく、うまく実験ができそうです。
ありがとうございます。
非常に些細なことではあるのですが、一つ指摘させていただきます。
programerではなくprogrammer、programingではなくprogrammingが正しい綴りです。
揚げ足取りのような指摘ですみません。
by 卯 2016/10/18 07:46
>卯さん
お褒めの言葉、そして誤りのご指摘、感謝です。
by Nobuo@管理人 2016/10/18 13:13