前回は「コンストラクタ(作るときの初期設定)」をやりました。

今回は、オブジェクト指向の山場、継承(けいしょう)です。あるクラスの性質を、別のクラスが受けつぐ仕組みです。少し難しく感じるかもしれませんが、考え方はとても自然です。

継承とは、あるクラスの性質を引きつぎ、新しいクラスを作ることです。

似たようなクラスを、何度も書きたくない

たとえば、「会社員」と「学生」のクラスを作るとします。

class Employee   // 会社員
{
    public string Name { get; set; }
    public int Age { get; set; }
    public void Greet() { Console.WriteLine(Name + "です"); }
    public string Company { get; set; }   // 会社名
}

class Student    // 学生
{
    public string Name { get; set; }
    public int Age { get; set; }
    public void Greet() { Console.WriteLine(Name + "です"); }
    public string School { get; set; }    // 学校名
}

よく見ると、NameAgeGreet() が、まったく同じです。会社員も学生も「人」なので、共通する部分が多いんですね。

これを2回書くのは、無駄です。もし Greet() を直したくなったら、2か所とも直すことに。クラスが増えれば、もっと大変になります。

共通する部分は、1回だけ書いて、使い回したい」。それを実現するのが、継承です。

共通部分を、親クラスにまとめる

まず、共通する部分を「人(Person)」というクラスにまとめます。

class Person          // 親クラス(共通部分)
{
    public string Name { get; set; }
    public int Age { get; set; }
    public void Greet() { Console.WriteLine(Name + "です"); }
}

そして、会社員と学生は、この Person を受けつぎます

class Employee : Person   // Person を継承
{
    public string Company { get; set; }   // 会社員だけの追加部分
}

class Student : Person    // Person を継承
{
    public string School { get; set; }    // 学生だけの追加部分
}

: Person の部分が、「Person を受けつぐ」という意味です。

これで、EmployeeStudent は、書いていないのに NameAgeGreet() を持っています。Person から受けついだからです。

Employee taro = new Employee();
taro.Name = "山田";        // Person から受けついだ
taro.Company = "ネスト";   // Employee 自身のもの
taro.Greet();              // 山田です(Person から受けついだ)

共通部分は Person に1回書くだけ。会社員・学生は、自分だけの追加部分を書けばいい。すっきりしました。

親と子、という関係

継承の関係は、よく「親子」でたとえられます。

  • 親クラス(基底クラス) … 受けつがれる元(Person)
  • 子クラス(派生クラス) … 受けつぐ方(Employee、Student)

子は、親の性質を受けついで生まれ、さらに自分だけの個性(Company、School)を加えます。子は親のものを使えますが、親は子のものを知りません。現実の親子と、少し似ていますね。

「〜は、〜の一種である」

継承を使っていいかの判断は、シンプルな問いで分かります。

「子は、親の一種か?」

  • 「会社員は、人の一種か?」→ はい。だから継承してOK
  • 「学生は、人の一種か?」→ はい。OK

逆に、これは変です。

  • 「会社は、人の一種か?」→ いいえ。だから Company を Person から継承するのはおかしい

AはBの一種である」が成り立つときだけ継承する。これが、継承を正しく使うコツです。料理でいうなら「カレーは料理の一種」「ラーメンは料理の一種」——共通の「料理」から受けつぐ、という感じです。

なぜ、継承すると良いのか

継承の利点は、2つあります。

ひとつは、これまで見たとおり、共通部分を1回で書けること。重複が減り、直すときも1か所で済みます。

もうひとつは、変更に強いこと。「人」に共通の機能を足したくなったら、Person に1つ書くだけ。それを継承している会社員も学生も、自動でその機能を持ちます。1か所の変更が、全体に行きわたる。これは、大きなプログラムでとても効いてきます。

つまずきポイント

初心者がやりがちなのが、何でも継承でつなごうとすることです。

継承は便利ですが、「AはBの一種か?」が成り立たないのに無理につなぐと、かえって分かりにくくなります。

class Engine : Car   // 「エンジンは車の一種」? → おかしい

エンジンは車の一種ではなく、車の部品です。こういう「持っている」関係(車はエンジンを持つ)は、継承ではなく、前にやったフィールドで表します。

class Car
{
    public Engine engine;   // 車は、エンジンを「持っている」
}

「一種である」なら継承、「持っている」ならフィールド。この区別が、きれいな設計の分かれ目です。

まとめ

継承は、あるクラスの性質を受けついで、新しいクラスを作る仕組み。

class Person { ... }              // 親クラス(共通部分)
class Employee : Person { ... }   // 子クラス(Person を受けつぐ)
  • 共通部分を親にまとめ、子は受けつぐ
  • 子は、親の性質+自分の個性を持つ
  • 使うのは「子は親の一種である」が成り立つときだけ
  • 「持っている」関係は、継承ではなくフィールドで

継承は、オブジェクト指向の大きな山場のひとつです。一度で完全に分からなくても大丈夫。「共通部分を、受けつぐ」——この感覚だけ、つかんでおいてください。