ポリモーフィズム:オブジェクト指向の強力な機能

docs

前回の記事で、オブジェクト指向の基本である「カプセル化」と「継承」について学んだよね。今回は、オブジェクト指向をさらにパワフルにする「ポリモーフィズム」(多様性)について、具体例を交えながらわかりやすく解説していくよ!

ポリモーフィズムってなんだ?

ポリモーフィズムを一言でいうと、「一つのものが、状況によっていろいろな形(振る舞い)に変化する」こと。ちょっと抽象的だよね。でも、これって実は日常生活でもよくあることなんだ。

たとえば、僕たちはみんな「人間」だよね。でも、ある時は「学生」として勉強し、ある時は「アルバイト」として働き、またある時は「友達」として遊んだりする。同じ「人間」という存在なのに、状況に応じて役割や振る舞いが変わる。これがポリモーフィズムの考え方に近いんだ。

プログラミングの世界だと、もっと具体的な例を挙げるとわかりやすいよ。

ポリモーフィズムの例:動物たち

以前、「継承」の記事で「動物」クラスとそこから派生した「犬」クラス、「猫」クラスの話をしたのを覚えているかな?

あの時の例を少し発展させてみよう。

Java

// 親クラス
class Animal {
    public void makeSound() {
        System.out.println("動物の鳴き声");
    }
}

// 子クラス:犬
class Dog extends Animal {
    @Override // アノテーション。このメソッドが親クラスのメソッドをオーバーライドしていることを示す
    public void makeSound() {
        System.out.println("ワンワン!");
    }
}

// 子クラス:猫
class Cat extends Animal {
    @Override
    public void makeSound() {
        System.out.println("ニャーニャー!");
    }
}

このコードでは、AnimalクラスにmakeSound()というメソッドがある。そして、DogクラスとCatクラスはそれを継承して、それぞれの鳴き声を出すようにmakeSound()メソッドをオーバーライドしているよね。これがポリモーフィズムの準備段階なんだ。

ポリモーフィズムの真骨頂!

さて、いよいよポリモーフィズムの出番だ。

Java

public class PolymorphismExample {
    public static void main(String[] args) {
        Animal myDog = new Dog(); // DogオブジェクトをAnimal型として扱う
        Animal myCat = new Cat(); // CatオブジェクトをAnimal型として扱う

        myDog.makeSound(); // 結果:ワンワン!
        myCat.makeSound(); // 結果:ニャーニャー!

        System.out.println("--- 動物園の鳴き声 ---");
        Animal[] animals = new Animal[2];
        animals[0] = new Dog();
        animals[1] = new Cat();

        for (Animal animal : animals) {
            animal.makeSound(); // それぞれの実際の型に応じたmakeSound()が呼び出される!
        }
    }
}

このコードのポイントを見てみよう。

  1. Animal myDog = new Dog();
    • これは「Dogクラスのインスタンス(実体)を生成して、それをAnimal型として扱いますよ」という意味なんだ。
    • 右辺のnew Dog()Dogオブジェクトが作られるんだけど、左辺の変数myDogは親クラスのAnimal型で宣言されているよね。
    • でも、myDog.makeSound()を呼び出すと、ちゃんとDogクラスでオーバーライドされた「ワンワン!」が出力されるんだ!
  2. Animal myCat = new Cat();
    • これも同じだね。CatオブジェクトをAnimal型として扱っている。
    • そしてmyCat.makeSound()を呼び出すと、「ニャーニャー!」と出力される。
  3. Animal[] animals = new Animal[2];
    • さらにすごいのがこれ!Animal型の配列を宣言して、その中にDogオブジェクトとCatオブジェクトを一緒に入れちゃってるんだ。
    • 配列の中身はすべてAnimal型として扱われる。でも、forループでそれぞれのanimal.makeSound()を呼び出すと、それぞれのインスタンスの実際の型(DogなのかCatなのか)に応じたmakeSound()メソッドが呼び出されるんだ!

これがポリモーフィズムのパワー!

vscodeで実行した結果

なんでポリモーフィズムが便利なの?

上の例で「すごい!」とは思ったけど、具体的に何が便利なんだろう?

  1. コードの汎用性が上がる
    • 上の動物園の例を見てみて。「この動物は何の鳴き声だろう?」って一つ一つif-else文で「もし犬ならワンワン、もし猫ならニャーニャー」って書くのは面倒だよね?
    • ポリモーフィズムを使えば、「とりあえずAnimal型のmakeSound()を呼び出せば、それぞれの動物が勝手に正しい鳴き声を出してくれる」というように、一律の処理で様々なオブジェクトに対応できるようになるんだ。
    • 新しい動物(例えばCowクラス)を追加しても、Animal型として配列に入れれば、makeSound()を呼び出すだけで対応できちゃう。既存のコードをほとんど変更する必要がないんだ。
  2. 保守性が向上する
    • コードの変更が必要になった場合でも、影響範囲を少なくすることができる。
    • 例えば、「動物の鳴き声」の出し方自体を修正する場合、各動物クラスのmakeSound()メソッドだけを修正すればよく、それを利用している他のコード(例えば動物園の管理システム)にはほとんど影響がない。
  3. 拡張性が高まる
    • 新しい機能を追加する時、既存のコードに手を加えずに新しいクラスを追加するだけで対応できることが多い。
    • 新しい動物が増えても、その動物クラスをAnimalを継承して作れば、既存のAnimal型の配列やリストに格納して処理できる。

もう少し専門的な話:アップキャストとダウンキャスト

上の例でAnimal myDog = new Dog();のように、子クラスのオブジェクトを親クラスの型で扱うことを「アップキャスト」と呼ぶんだ。これはJavaが自動的にやってくれるので、特に意識する必要はないよ。

じゃあ、逆に親クラスの型で扱っているものを、子クラスの型に戻すことを「ダウンキャスト」というんだ。これは明示的に型変換を行う必要があるよ。

Java

Animal someAnimal = new Dog(); // アップキャスト

// ダウンキャスト
if (someAnimal instanceof Dog) { // 安全にダウンキャストするために、instanceofで型を確認する
    Dog specificDog = (Dog) someAnimal;
    specificDog.bark(); // Dogクラス独自のメソッドを呼び出す(例:bark()はDogにしかないと仮定)
}
  • instanceof演算子は、あるオブジェクトが特定のクラスのインスタンスであるかどうかをチェックするためのものだよ。これを使わないと、実行時にエラー(ClassCastException)になる可能性があるから注意してね。

まとめ

ポリモーフィズムは、オブジェクト指向の三大要素(カプセル化、継承、ポリモーフィズム)の一つで、プログラムの柔軟性と拡張性を飛躍的に向上させる強力な機能だよ。

  • 一つのインターフェースで複数の実装を扱える:同じメソッド名なのに、オブジェクトの実際の型によって異なる振る舞いをする。
  • コードの汎用性が高まり、保守性・拡張性が向上する

最初はちょっと難しく感じるかもしれないけど、具体的な例を動かしてみて、その便利さを実感することが一番の近道だよ。ぜひ、自分で色々なクラスを作って試してみてね!

コメント

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