前回は「ポリモーフィズム(同じ命令で違う動き)」をやりました。

今回は、オブジェクト指向編の締めくくり。抽象クラスインターフェースです。「形だけ決めて、中身は各クラスに任せる」という考え方です。少し抽象的ですが、ここまで来たなら大丈夫です。

抽象クラスとインターフェースは、「やるべきこと」だけを決めて、「やり方」は各クラスに任せる仕組みです。

「あいさつは必ずする。でも、やり方は自由」

前回までの Person を思い出してください。子クラスたちは、それぞれ Greet() を上書きしていました。

ここで、こう考えます。

「Person を継承するなら、あいさつは必ず作ってほしい。でも、その中身は、各クラスに任せる

つまり、

  • 「Greet という処理を持つこと」は強制したい
  • でも「Greet の中身」は決めない(各クラスが自由に)

この「形だけ決めて、中身は任せる」を実現するのが、抽象クラスとインターフェースです。

抽象クラス ── 中身のないメソッドを持てる

抽象クラスは、abstract(アブストラクト)という印をつけたクラスです。中に「中身のないメソッド」を置けます。

abstract class Person
{
    public string Name { get; set; }

    public abstract void Greet();   // 中身がない!宣言だけ
}

public abstract void Greet(); を見てください。{ }(中身)がなく、いきなり ; で終わっています。これは「Greet というメソッドを持て。でも中身は、継承する側が書け」という意味です。

継承する子クラスは、この Greet を必ず書かなければいけません。

class Employee : Person
{
    public override void Greet()   // 書かないとエラー
    {
        Console.WriteLine("会社員の" + Name + "です");
    }
}

書き忘れると、エラーになります。「あいさつは必ず作って」という強制が効いているんです。

抽象クラスは、new できない

もうひとつ特徴があります。抽象クラスは、それ自体は実物を作れません

Person p = new Person();   // エラー!抽象クラスは new できない

考えてみれば当然です。Person の Greet は中身がない(未完成)のだから、そのままでは動きようがありません。「人」というぼんやりした概念であって、実物にはできない。実物にできるのは、中身を書いた Employee や Student だけ、というわけです。

インターフェース ── 形だけの「約束」

インターフェースは、抽象クラスをもっと突き詰めたものです。中身のあるものは一切持たず、形(やるべきこと)だけを決めます

interface IGreetable
{
    void Greet();   // 形だけ
}

interface(インターフェース)で定義します。中身はいっさいなし。「Greet というメソッドを持つこと」だけを約束します。

(C#では、インターフェースの名前は I で始める慣習があります。IGreetable のように。)

これを使うときは、: でつなぎます。

class Robot : IGreetable
{
    public void Greet()   // 約束どおり、Greet を実装
    {
        Console.WriteLine("ピピッ、ロボットです");
    }
}

Robot は Person とは無関係ですが、「あいさつできる(IGreetable)」という約束は守っています。

抽象クラスと、インターフェースの違い

2つは似ていますが、役割が少し違います。

  • 抽象クラス … 「〜の一種である」関係。共通の中身も持てる(Person を継承する Employee)
  • インターフェース … 「〜ができる」という約束。中身は持たない(あいさつできる、飛べる、など)

たとえるなら、

  • 抽象クラスは「血筋」… 人という種類を受けつぐ
  • インターフェースは「資格・能力」… あいさつできる、という能力を持つ

人でもロボットでも、種類は違っても「あいさつできる」という能力(インターフェース)は、共通して持てる。これがインターフェースの柔軟なところです。

なぜ、こんな仕組みがあるのか

「中身を書かないメソッド」なんて、何の役に立つのか。理由は、前回のポリモーフィズムと繋がります

「Greet を必ず持つ」と約束させておけば、中身が何であれ、安心して Greet() を呼べるんです。

List<IGreetable> items = new List<IGreetable>();
items.Add(new Employee());
items.Add(new Robot());

foreach (IGreetable item in items)
{
    item.Greet();   // 全員 Greet を持つと約束済みなので、安心して呼べる
}

「あいさつできる」と約束したものだけを集めたので、全員に Greet() を呼べる。会社員でもロボットでも。約束(形)を決めることで、ポリモーフィズムが安全に使えるんです。

つまずきポイント

最初は、「中身を書かないものに、何の意味が?」と感じて当然です。

ポイントは、抽象クラスやインターフェースは、それ単体で役に立つものではないということ。「みんなに共通のルールを決める」ための仕組みです。

「全員、あいさつできること」とルールを決めておくと、後でまとめて扱うときにラクになる。いま完全に分からなくても、大丈夫です。「形だけ決めて、中身は任せる、というやり方があるんだな」と知っておけば、実際に必要になったとき「あれか」と思い出せます。

まとめ

抽象クラスとインターフェースは、「やるべきこと」を決めて「やり方」を任せる仕組み。

  • 抽象クラスabstract)… 中身のないメソッドを持てる。「〜の一種」の関係。new できない
  • インターフェースinterface)… 形だけの約束。「〜ができる」という能力
  • 子クラスは、決められたメソッドを必ず書く
  • 共通のルールを決めることで、ポリモーフィズムが安全に使える

これで、オブジェクト指向(クラス・継承・オーバーライド・ポリモーフィズム・抽象化)が、ひととおり揃いました。ここまでお疲れさまでした。大きな山を越えました。