【Java入門】継承とポリモーフィズムをやさしく解説|コード再利用と多態性の基本

Java入門・実践
スポンサーリンク

「継承とポリモーフィズム」って、名前のクセが強いなあ!

Javaの学習を始めたばかりの頃、テキストに出てきた言葉たち。

その中でも、ひときわインパクトが強かったのがこの2つ。

「継承」

「ポリモーフィズム」

読めるけど意味が分からない。

どこかの魔法学校で習う呪文かな?と思ったのは私だけではないはずです。

本を読んでみても、「コードの再利用性が〜」「多態性によって〜」と説明され、(いや、だから“ポリモーフィズム”って何なの…)

と、ページを閉じたのも一度や二度ではありません。

でも、ちょっとずつコードを書いて、試して、つまずいて、 そのたびに「なるほど、そういうことか!」と点が線に変わる瞬間がありました。

この記事では、そんな私の“理解できなかった頃のモヤモヤ”を思い出しながら、Javaにおける継承とポリモーフィズムの基礎を、実例を交えて解説していきます。

「とりあえず extends って書いとけばいいんでしょ?」という頃の私のようなあなたに、 「設計ってちょっとおもしろいかも」と思える入り口になれば嬉しいです。

スポンサーリンク
  1. 継承?ポリモーフィズム?聞き慣れない言葉がやってきた
    1. 突然あらわれた「継承」と「ポリモーフィズム」
    2. 「オブジェクト指向ってなんかすごそう」から始まる道
    3. 実はあなたも「継承の恩恵」を受けている?
    4. この章のまとめ:まずは“ことばの壁”を超えてみよう
  2. 【継承入門】親から子へ、コードを受け継ぐしくみ
    1. 継承って、どういう仕組み?
    2. 親(スーパークラス)と子(サブクラス)の関係
    3. 継承のメリットは「コードの再利用」
    4. 継承の注意点:「なんでも継承」はNG!
    5. この章のまとめ:継承の“うまい使い方”を身につけよう
  3. 【オーバーライド入門】親のメソッド、書き換えていいんです
    1. オーバーライドとは「自分流にアレンジできる仕組み」
    2. 例:動物の鳴き声をカスタマイズしたい!
    3. @Override アノテーションってなんのため?
    4. オーバーライドのすごいところ=「同じ命令で違う動作をさせられる」
    5. オーバーライドでよくあるミス
    6. この章のまとめ:自分だけの動きを定義できるのがオーバーライド!
  4. 【ポリモーフィズム入門】同じメソッド名、でも動きが変わる?
    1. 「ポリ…なに?言いにくさ世界選手権」の代表選手
    2. ポリモーフィズム=“同じメッセージに、違う反応を返す力”
    3. どうしてこんなことができるの?
    4. 例:動物たちを一括処理したいときに便利!
    5. よくある混乱:「オーバーロードと何が違うの?」
    6. この章のまとめ:多様性は強さ!
  5. 継承とポリモーフィズムはどう使い分けるの?
    1. 「継承でいいのか?それとも別の手段があるのか?」
    2. 継承は「is-a」関係のときに使う!
      1. 継承が適切な例
      2. 継承が不自然な例
    3. 「コードを共有したいだけなら継承しない」が正解のときもある
    4. 継承 vs 委譲(Composition)のざっくり比較
    5. 設計で迷ったときのチェックリスト
    6. まとめ:道具を正しく選べば、設計はもっと心地よくなる!
  6. まとめ:今日からクラス設計の見え方が変わる
    1. 「クラス設計って、こう考えるんだ」が見えてきた!
    2. このシリーズで学んだことをざっくりおさらい
    3. 設計に“正解”はない。だからこそ「なぜそうしたか」が大切

継承?ポリモーフィズム?聞き慣れない言葉がやってきた

突然あらわれた「継承」と「ポリモーフィズム」

Javaを学び始めて少し経った頃、クラスやメソッドにも慣れてきて、 「少しは成長したかも…!」と思った矢先にやってきたのがこの2語。

継承と、ポリモーフィズム。

いや、聞き慣れなさすぎませんか?

カタカナの主張が強すぎて、ついWikipediaを開いたものの、「多態性により、型の抽象化を実現する」 「親クラスの特性を子クラスが…」

…読んだはずなのに脳内で霧が晴れないあの感覚。

私も何度味わったことか。

「オブジェクト指向ってなんかすごそう」から始まる道

そもそも「オブジェクト指向」自体が、ちょっと遠い世界の話に思えがちです。

でも実際には、とてもシンプルな目的を持った考え方なんです。

それはズバリ、「複雑なプログラムを、分かりやすく安全に作るための発明」。

  • 繰り返し同じコードを書くのが大変 → 再利用できる仕組みが欲しい
  • 似たような機能でも、ちょっとだけ挙動を変えたい → 柔軟に拡張できる方法が欲しい

そんな “現場のリアルな悩み” に向き合って生まれたのが、継承(Inheritance)とポリモーフィズム(Polymorphism) という発想なんです。

実はあなたも「継承の恩恵」を受けている?

Javaを書いていると、知らないうちに使っていることがあります。

Java
public class MyList extends ArrayList<String> {
    // ArrayListの便利機能がそのまま使える!
}

この extends というキーワードこそ、継承の証。

ArrayList という「親クラス」の機能を、「子クラス」が丸ごと受け継いで使っています。

「え、じゃあ最初から全部親に任せとけばいいじゃん」と思うかもしれませんが、それだけではダメ。

そこに「必要な部分だけカスタマイズする」という工夫が登場し、ポリモーフィズムの役割が見えてきます。

この章のまとめ:まずは“ことばの壁”を超えてみよう

  • 「継承」=親の力を受け継ぐ仕組み
  • 「ポリモーフィズム」=共通のルールで、違う振る舞いを持たせる仕組み
  • どちらも、コードの柔軟性・再利用性を高めるための知恵

【継承入門】親から子へ、コードを受け継ぐしくみ

継承って、どういう仕組み?

継承(Inheritance)とは、あるクラスが別のクラスの機能を引き継ぐことを指します。

Javaでは、extends というキーワードで実現できます。

Java
public class Animal {
    void speak() {
        System.out.println("なにかしゃべるよ!");
    }
}

public class Dog extends Animal {
    // ここには speak() がなくても…
}

Dog pochi = new Dog();
pochi.speak(); // → 「なにかしゃべるよ!」と出力される!

そう、DogクラスはAnimalクラスのspeak()を“受け継いで”使えるんです。

これが、継承の基本的な使い方。

親(スーパークラス)と子(サブクラス)の関係

Javaでは、

  • 継承される元のクラスを「スーパークラス(親)」
  • 継承する側のクラスを「サブクラス(子)」 と呼びます。

Dog extends Animal なら、Animalが親で、Dogが子。

まるで「親の性格をちょっと引き継いで、でも自分らしく成長する子」みたいなイメージです。

継承のメリットは「コードの再利用」

継承の大きな目的は、重複するコードを避けて、共通の処理をまとめることです。

例:複数の動物がいる場合、それぞれのクラスに eat() や sleep() を毎回書くのは面倒ですよね?

そこで、こうします

Java
public class Animal {
    void eat() {
        System.out.println("もぐもぐ…");
    }
    void sleep() {
        System.out.println("すやすや…");
    }
}

public class Cat extends Animal {
    void meow() {
        System.out.println("にゃーん");
    }
}

Cat tama = new Cat();
tama.eat();   // 親から継承
tama.meow();  // 子だけのオリジナル

共通する処理は親に集約して、個性は子に任せる。 これが継承を使った“うまい設計”です。

継承の注意点:「なんでも継承」はNG!

「便利だから何でも継承しよう!」と考えると、 やがて “謎の親に頼りすぎたワカメ構造” になります。

  • 意味のない親子関係(例:Printer extends Sandwich)
  • 親の修正が子に無関係でも影響する
  • 子の責務が不明瞭になる

こんなときは、「それ、本当に“is-a”の関係かな?」と立ち止まってみましょう。

たとえば、

  • 「猫は動物である」→ Cat is an Animal(継承OK)
  • 「自転車は車である」→?微妙…
  • 「プリンタはサンドイッチである」→ダメ!

この章のまとめ:継承の“うまい使い方”を身につけよう

  • extends を使えば、クラスを「親 → 子」へ拡張できる
  • 継承は 共通処理をまとめる&再利用するのが基本
  • 「is-a関係」が成り立つときに継承を使うのが◎
  • 親に詰め込みすぎたり、意味のない継承は避けるべし

【オーバーライド入門】親のメソッド、書き換えていいんです

オーバーライドとは「自分流にアレンジできる仕組み」

継承によって、子クラスは親クラスの機能をまるっと使えるようになりました。

でも…「このまま使うにはちょっと都合が悪いんだけど…」という場面、実は結構あるんです。

そんなときに登場するのが、オーバーライド(Override)。

読んで字のごとく、親クラスのメソッドを“上書きして、自分流に変える”ための機能です。

例:動物の鳴き声をカスタマイズしたい!

Java
public class Animal {
    void speak() {
        System.out.println("何かが鳴いています。");
    }
}

public class Dog extends Animal {
    @Override
    void speak() {
        System.out.println("ワンワン!");
    }
}

public class Cat extends Animal {
    @Override
    void speak() {
        System.out.println("ニャー!");
    }
}

このように、親クラスのspeak()を子クラス側で同じシグネチャ(戻り値・引数・名前)で書き直すことで、「上書き」ができます。

@Override アノテーションってなんのため?

Javaでは、オーバーライドするメソッドの上に

Java
@Override

と書くのが慣習です。

これは単なる飾りではなく、「これは親クラスのメソッドをちゃんと上書きしていますよ」と教えてくれる目印。

もし、名前が違ったり、引数を間違えてたりしていると…ちゃんとエラーで教えてくれます。

Java
// ミスしてる例(名前が間違ってる)
@Override
void speek() {
    System.out.println("間違った綴り…");
}
// → エラーになる!(ありがたい)

つまり、@Overrideは“正しく上書きできてるかチェックしてくれるセーフティネット”みたいな存在です。

オーバーライドのすごいところ=「同じ命令で違う動作をさせられる」

Java
Animal a1 = new Dog();
Animal a2 = new Cat();
a1.speak(); // → ワンワン!
a2.speak(); // → ニャー!

見てください。どっちもAnimal型で、speak()を呼んでるだけ。

でも、中身がDogかCatかによって振る舞いが変わってる!

これがまさに、次章で詳しく説明する「ポリモーフィズム(多態性)」の準備なんです!

オーバーライドでよくあるミス

  • 戻り値や引数が親と違っている(正しく上書きされない)
  • アクセス修飾子の制限:親より厳しくしてはいけない!
     例:親がpublicなら、子はprotectedやdefaultにできない
  • staticメソッドはオーバーライドではなく「隠蔽」になる(このあたりは応用編で)

この章のまとめ:自分だけの動きを定義できるのがオーバーライド!

  • オーバーライドとは、親クラスのメソッドを“同じ名前で”上書きすること
  • @Override を書くことで、安全かつミスに気づける
  • 同じ型でも中身によって違う動作をさせる準備になる(=ポリモーフィズムへの道)

【ポリモーフィズム入門】同じメソッド名、でも動きが変わる?

「ポリ…なに?言いにくさ世界選手権」の代表選手

「ポリモーフィズム(Polymorphism)」

――初めて出会ったとき、「舌噛みそう」と思ったのは私だけじゃないはずです。

そして説明を読んでもたいていはこうなります。

「多態性のこと。あるクラスが共通のインターフェースを通じて異なる動作をすること。」

…え?つまりどういうこと?

この章では、そんな言葉の壁を取り払いながら、「ポリモーフィズム」の正体をゆっくり丁寧に見ていきます!

ポリモーフィズム=“同じメッセージに、違う反応を返す力”

この現象、実はすでにあなたも経験しています。

Java
Animal a1 = new Dog();
Animal a2 = new Cat();

a1.speak(); // → ワンワン!
a2.speak(); // → ニャー!

ここで注目してほしいのが、「a1 も a2 も型は Animal」だということ。

つまり “Animal型という共通のルール”に従ってコードを書いているのに、中身に応じて違う動きになる。

これこそがポリモーフィズムなんです!

「ポリモーフィズム」とは、同じメソッド名で異なる動作を実現するテクニック。

どうしてこんなことができるの?

答えは、前の章で紹介した オーバーライド にあります。

親クラスで定義されたメソッドを、子クラスで自分用に上書き(オーバーライド)することで、 “親として扱っているのに、子の個性が出せる” という現象が起こります。

この仕組みによって、以下のようなメリットが得られます。

  • コードの柔軟性が高まる:追加されるクラスに対して、親クラス側のコードを変えずに対応できる
  • 統一的に処理を書ける:同じ型で扱えるので、for文やメソッド引数にも柔軟に使える

例:動物たちを一括処理したいときに便利!

Java
Animal[] zoo = { new Dog(), new Cat(), new Bird() };

for (Animal animal : zoo) {
    animal.speak();
}

ここではすべて Animal 型の配列にしていますが、 それぞれの中身に応じて Dog → ワンワン!、Cat → ニャー!、Bird → ピヨピヨ! と振る舞いが変化します。

「動物たち、発声せよ!」と命令するだけで、それぞれが自分らしい反応を返す。

これがポリモーフィズムの“魔法”です!

よくある混乱:「オーバーロードと何が違うの?」

オーバーロードとポリモーフィズムは、よく混同されがちです。

簡単に整理すると…

比較項目オーバーロードポリモーフィズム
発生場所同じクラス内で複数のメソッド定義親子関係を利用して、子クラスで振る舞いを変える
メソッド名同じ同じ
引数 異なる(数や型) 同じ
実行時 or コンパイル時コンパイル時に決定実行時に決定(動的バインディング)

この章のまとめ:多様性は強さ!

  • ポリモーフィズムは「共通の型で、違う振る舞いを引き出せる」しくみ
  • オーバーライドによって、それぞれのクラスに“個性”を与えることができる
  • 統一的なコードで拡張性・柔軟性の高い設計が可能になる
  • オーバーロードとの違いにも注意!(決まるタイミングが違うよ)

継承とポリモーフィズムはどう使い分けるの?

「継承でいいのか?それとも別の手段があるのか?」

ここまで、継承とポリモーフィズムがどれほど便利かを見てきました。

でも、いざ自分で設計しようとするとこうなりがちです。

「このクラス、親から継承させるべき?」

「でも、無理やり継承すると逆にややこしいような…?」

そう、“継承の便利さ”と“使いどころ”は別問題。

この章では、「継承すべき場面」と「やめた方がいい場面」を見極めるための考え方をご紹介します!

継承は「is-a」関係のときに使う!

Javaの継承は、「〇〇は△△の一種である(is-a)」という関係が成り立つときに自然に使えます。

継承が適切な例

Java
class Animal {
    void eat() {}
}

class Dog extends Animal {
    void bark() {}
}
  • DogはAnimalである(Dog is an Animal)→ OK!

継承が不自然な例

Java
class Printer {
    void print() {}
}

class Report extends Printer {
    void generate() {}
}
  • ReportはPrinterではない → 無理やりすぎる…

こういうときは、無理せず別の方法(委譲や共通インターフェース)を検討しましょう。

「コードを共有したいだけなら継承しない」が正解のときもある

「同じ処理を共有したい」=すぐ継承ではありません!

たとえば、DogとCatが同じwalk()メソッドを必要としていても、 必ずしもそれらをAnimalにまとめる必要はありません。

代わりに、「歩くという行動を持つクラスに処理を“委譲”する」という考え方があります。

Java
class Walker {
    void walk() {
        System.out.println("てくてく歩く");
    }
}

class Dog {
    Walker walker = new Walker();
    void walk() {
        walker.walk();
    }
}

このように、クラスの責務を分けて「委譲(Composition)」することで柔軟性を保つ設計ができます。

継承 vs 委譲(Composition)のざっくり比較

観点継承(Inheritance)委譲(Composition)
関係性is-a(〜は〜である)has-a(〜は〜を持っている)
柔軟性低い(親の変更に影響)高い(差し替えやすい)
再利用のしやすさ手軽に機能をまとめられる関心ごとを分離できてテストしやすい
変更への強さ弱い(親子が密結合)強い(緩くつながる)

設計で迷ったときのチェックリスト

以下のような問いかけをすると、継承すべきか判断しやすくなります。

  • ⭕️ 「〇〇は△△の一種である」と言えるか?
  • ⭕️ 親クラスのすべての機能を本当に必要としているか?
  • ⭕️ 子クラスを増やしても、設計が破綻しないか?
  • ❌ コードを共有したいだけで、継承していないか?
  • ❌ 親クラスに変更があると、子クラスが壊れそうではないか?

まとめ:道具を正しく選べば、設計はもっと心地よくなる!

  • 継承は便利だけど「どこでも使えばOK」ではない
  • 本当に「is-a」の関係か?と立ち止まるクセをつけよう
  • 継承だけでなく「委譲」や「インターフェース」も柔軟な設計には有効!
  • 設計に正解はないけれど、「読みやすさ・変更のしやすさ」を意識するのが大切

まとめ:今日からクラス設計の見え方が変わる

「クラス設計って、こう考えるんだ」が見えてきた!

最初はまるで呪文のように感じた「継承」や「ポリモーフィズム」も、 コードと一緒に見てみると、「なんか使えそうかも」という感覚がつかめてきたのではないでしょうか。

  • 「親から受け継ぐことでコードをすっきり書ける」
  • 「同じメソッド名で振る舞いを変えることができる」
  • 「でも、便利だからといって何でも継承してはいけない」

そう、クラス設計には“選ぶチカラ”が必要なんです。

そしてその選択肢のひとつが、「継承」や「ポリモーフィズム」というわけですね。

このシリーズで学んだことをざっくりおさらい

学んだこと

  • 継承・ポリモーフィズムという概念の“壁”は、ちゃんと乗り越えられる!
  • extends を使って、親から機能を受け継げる
  • オーバーライドで、親のメソッドを自分流にアレンジできる
  • ポリモーフィズムは「共通の型で違う動きをさせる」強力な仕組み
  • 継承の使いすぎには注意!必要なら委譲やインターフェースを選ぶことも設計力

この流れを通して、「書くだけでなく、設計する」視点が少しずつ身についたはずです!

設計に“正解”はない。だからこそ「なぜそうしたか」が大切

クラス設計は一言で言えば“選び方の芸術”です。

  • このクラス、継承すべきか?
  • 共通処理は親に書く?それとも外部に任せる?
  • 将来的な変更にも耐えられる構造になっているか?

初心者のうちは難しく感じるかもしれませんが、「なぜその設計をしたのか」を言葉にできることが何より大切。

それができると、コードの“読みやすさ”も“保守性”もぐっと上がっていきますよ。

decopon
decopon

最初はとっつきにくい「継承」や「ポリモーフィズム」も、実は「コードと仲良くなるための知恵」だった—— そんなふうに感じていただけていたら、この記事を書いた意味があったなと思います。

私自身、学び始めた頃は「ポリモー…なんだっけ?」と立ち止まったり、 extends とか @Override にビクビクしながらコードを書いたりしていました。 でも少しずつ、仕組みを知って、使って、つまずいて、また書いて—— そうやって育てていく実感こそが、プログラミングの一番おもしろいところだと思っています。

コメント

タイトルとURLをコピーしました