Day 42 のゴールと全体像
Day 42 のテーマは virtual・override と多態性(ポリモーフィズム)です。 継承を一歩進めて、
- 仮想メソッド(
virtual) - オーバーライド(
override) - 多態性(ポリモーフィズム)
という、オブジェクト指向の「おいしいところ」に入っていきます。
ざっくり言うと、
「同じメソッド名なのに、クラスごとに違う動きをさせる仕組み」
を理解していく回だと思って読んでいただけると、 スッと入ってきやすいです。
仮想メソッド(virtual)とは何か
「子クラスで上書きしていいですよ」という宣言
まずは 仮想メソッド(virtual メソッド) からです。
virtual は、
「このメソッドは、派生クラスで上書き(オーバーライド)してもいいですよ」
という意味のキーワードです。
例として、動物クラスを考えてみます。
using System;
// 基底クラス:動物
class Animal
{
public string Name { get; set; }
public Animal(string name)
{
Name = name;
}
// 仮想メソッド:鳴く
public virtual void Speak()
{
Console.WriteLine($"{Name} は何か鳴きます。");
}
}
C#ここでのポイントは、
public virtual void Speak()→ 「このSpeakは、子クラスで上書きしてもよいメソッドですよ」という宣言
です。
オーバーライド(override)とは何か
「親のメソッドを、自分流に書き換える」
次に オーバーライド(override) です。
override は、
「基底クラスの仮想メソッドを、派生クラスで自分流に書き換える」
ためのキーワードです。
先ほどの Animal を継承して、 犬と猫のクラスを作ってみます。
// 派生クラス:犬
class Dog : Animal
{
public Dog(string name) : base(name)
{
}
// Speak を犬用にオーバーライド
public override void Speak()
{
Console.WriteLine($"{Name} は『ワン!』と鳴きます。");
}
}
// 派生クラス:猫
class Cat : Animal
{
public Cat(string name) : base(name)
{
}
// Speak を猫用にオーバーライド
public override void Speak()
{
Console.WriteLine($"{Name} は『ニャー!』と鳴きます。");
}
}
C#ここでのポイントは、
public override void Speak()→ 基底クラスAnimalのvirtual Speakを「上書き」している- メソッド名・戻り値・引数の形は、基底クラスと同じでなければならない
というところです。
多態性(ポリモーフィズム)を体感する
「同じ型として扱っているのに、動きは違う」
virtual と override の真価は、 「多態性(ポリモーフィズム)」を使ったときに現れます。
多態性とは、
「同じ型として扱っているのに、実際の中身によって動きが変わること」
です。
先ほどの Animal・Dog・Cat を使って、 こんなコードを書いてみます。
class Program
{
static void Main(string[] args)
{
// Animal 型の変数に、Dog と Cat のインスタンスを入れる
Animal a1 = new Dog("ポチ");
Animal a2 = new Cat("タマ");
// 同じように Speak を呼んでいるのに、実際の動きは違う
a1.Speak(); // ポチ は『ワン!』と鳴きます。
a2.Speak(); // タマ は『ニャー!』と鳴きます。
Console.ReadLine();
}
}
C#ここが、多態性の一番おもしろいところです。
- 変数の型はどちらも
Animal - 呼び出しているメソッドはどちらも
Speak() - なのに、実際の動きは「犬の鳴き方」「猫の鳴き方」に変わる
これは、
- 基底クラスで
virtualを宣言し - 派生クラスで
overrideしている
からこそ実現できる動きです。
重要ポイント:
- 多態性は「共通のインターフェース(メソッド名)で扱いつつ、 実際のクラスごとに違う振る舞いをさせる」ための仕組みです。
なぜ多態性がうれしいのか
1. 共通の処理を書きやすくなる
例えば、動物がたくさんいる動物園を考えてみます。
class Zoo
{
private readonly List<Animal> animals = new List<Animal>();
public void AddAnimal(Animal animal)
{
animals.Add(animal);
}
public void MakeAllAnimalsSpeak()
{
Console.WriteLine("動物園の動物たちが一斉に鳴きます。");
foreach (var animal in animals)
{
animal.Speak(); // 型は Animal だが、実際のクラスごとに違う鳴き方をする
}
}
}
C#使う側はこうです。
class Program
{
static void Main(string[] args)
{
Zoo zoo = new Zoo();
zoo.AddAnimal(new Dog("ポチ"));
zoo.AddAnimal(new Cat("タマ"));
zoo.AddAnimal(new Dog("シロ"));
zoo.MakeAllAnimalsSpeak();
Console.ReadLine();
}
}
C#ここでは、
Zooは「Animal のリスト」だけを意識している- 実際にどんな動物が入っているかは気にしない
Speak()を呼べば、それぞれの動物が自分らしく鳴いてくれる
という構造になっています。
これが、多態性の力です。
2. 新しいクラスを追加しても、既存コードをほとんど変えなくてよい
例えば、鳥クラス Bird を追加したくなったとします。
class Bird : Animal
{
public Bird(string name) : base(name)
{
}
public override void Speak()
{
Console.WriteLine($"{Name} は『チュンチュン』と鳴きます。");
}
}
C#このとき、
Zooクラスは一切変更しなくてよいMakeAllAnimalsSpeak()はそのまま動く- ただ
zoo.AddAnimal(new Bird("ピーちゃん"));と追加するだけで、 鳥も一緒に鳴いてくれるようになります。
重要ポイント:
- 多態性を使うと、「新しい種類」を追加するときに、 既存の処理をほとんど変えずに済みます。
- これは、アプリが大きくなったときに、 保守性・拡張性を大きく高めてくれる考え方です。
virtual・override の基本テンプレート
最後に、virtual・override・多態性の基本形をテンプレートとしてまとめます。
基底クラス
class Base
{
public string Name { get; set; }
public Base(string name)
{
Name = name;
}
// 仮想メソッド
public virtual void DoWork()
{
Console.WriteLine($"{Name} が基本的な仕事をします。");
}
}
C#派生クラスA
class DerivedA : Base
{
public DerivedA(string name) : base(name)
{
}
// オーバーライド:A 用の仕事に書き換える
public override void DoWork()
{
Console.WriteLine($"{Name} が A としての仕事をします。");
}
}
C#派生クラスB
class DerivedB : Base
{
public DerivedB(string name) : base(name)
{
}
// オーバーライド:B 用の仕事に書き換える
public override void DoWork()
{
Console.WriteLine($"{Name} が B としての仕事をします。");
}
}
C#多態性を使う側
class Program
{
static void Main(string[] args)
{
Base b1 = new DerivedA("オブジェクトA");
Base b2 = new DerivedB("オブジェクトB");
// 型は Base だが、実際のクラスごとに違う動きをする
b1.DoWork(); // A としての仕事
b2.DoWork(); // B としての仕事
Console.ReadLine();
}
}
C#このテンプレートをベースに、
- 「共通のメソッド名」を基底クラスに
- 「クラスごとの違う動き」を派生クラスに
という形で、自分なりの例を作ってみると、 virtual・override・多態性の感覚がぐっと身近になります。
Day 42 のまとめ
Day 42 では、
- 仮想メソッド(
virtual) → 「子クラスで上書きしてよいメソッド」の宣言 - オーバーライド(
override) → 「親の仮想メソッドを、自分流に書き換える」仕組み - 多態性(ポリモーフィズム) → 「同じ型・同じメソッド名なのに、実際のクラスごとに違う動きをする」性質
を、動物の例やテンプレートを通して学びました。
多態性は、オブジェクト指向の中でも特に強力で、
「新しい種類を追加しても、既存のコードをほとんど変えなくてよい」
という、大きなメリットをもたらしてくれます。
ぜひ、
- 自分で基底クラスを1つ作り
- 派生クラスを2〜3個作って
virtual・overrideを使って「同じメソッド名で違う動き」を試してみる
という小さな練習を通して、 多態性を 「教科書の言葉」ではなく、 自分のコードの中で生きている感覚として、少しずつ育てていってください。
