19.0429

多態性(polymorphism)

多態性/多様性/polymorphism とは、
  1. 同じinterface を、使っても、実装する class によって、多様にアクションすること.
  2. メソッドを、オバーライド した時、「子供class is 親クラスclass」 を利用して、
    同じメソッド であっても、実際の class によって、多様にアクションすること.
です。

この章では、「2」 の、
  同じメソッド でも、実際のクラスによって、アクションを選んで実行されること
この意味で、考えてみましょう。

前回の、theRest()メソッド を、オーバーライド して、そのメソッドが、
実際の class で、多様にアクションすること を、見ていきましょう。
こんなコードにしてみました


その前に、今回使う
  is演算子
を、マスターしましょう。
リファレンスのページは、ここです
また、Stringインスタンス の、プロパティ:length についてのページは、ここです。
length とは、Stringインスタンス の、プロパティ で、
 その 文字列/Stringインスタンス の、「文字数: Int」 です。
 アクセスするには、「Stringインスタンス.length」 でした。)

  is and !is Operators: is と !is演算子

We can check whether an object conforms to a given type at runtime by using the is operator or its negated form !is:
is演算子 または その否定形である、!is演算子 を使って、あるオブジェクト が、与えられた型 に、一致するかどうかを、プログラム実行時に、確かめることが出来ます。

  ---------------------------------ー
// obj が、String型 であれば、その文字列の長さ を コンソール に出力.
if (obj is String) {
    print(obj.length)
}

  ----------------------------------

// obj が、String型 でない時は、「Not a String」 を、出力.
if (obj !is String) { // same as !(obj is String)
    print("Not a String")
}
else {	// そうでない時は(String型の時は)、その文字列の長さ を コンソール に出力
    print(obj.length)
}

それでは、このコード を、見て多態性について考えてみます

まず、このコードのおおざっぱな流れです:
  1行目:
    Carクラス を定義.(親クラス)
    コンストラクタで、maxGasoline/最大ガソリン量: Double を、定義。
    theRest/残ガソリン量(drive/走った距離: Double): Double を、定義。
      / 計算方法は、「 残ガソリン量 = 最大ガソリン量 - (走った距離 ÷ 燃費) 」 です。

  8行目:
    DeluxeCarクラス を Carクラス を継承して、定義.(親クラスCar の、子供クラス)
    コンストラクタで、maxGasoline: Double を、引き継いでいます
    theRest(drive: Double): Double を、override
      / DeluxeCarクラス は、燃費が悪いです、、よって、override。

  15行目:
    Jeepクラス を Carクラス を継承して、定義.(親クラスCar の、子供クラス)
    コンストラクタで、maxGasoline: Double を、引き継いでいます
    theRest(drive: Double): Double を、override
      / Jeepクラス は、Carクラスより燃費が悪いです、、よって、override。

  22~25行目:main()関数:
    Carクラス の、プロパティmaxGasoline(最大ガソリン量) を、
     40.0(リットル) に定義して、インスタンスcar を、作成。
       / car は、小文字始まりなので、Car とは、「違う名前」 です。

    DeluxeCarクラス の、プロパティmaxGasoline(最大ガソリン量) を、
     60.0(リットル) に定義して、インスタンスdeluxeCar を、作成。

    Jeepクラス の、プロパティmaxGasoline(最大ガソリン量) を、
     50.0(リットル) に定義して、インスタンスjeep を、作成。

  子供クラス is 親クラス(子供クラスは、親クラスである)
    / しかし、「親クラス は、子供クラス である」 は、間違っています。

これの意味は、
  猫 は、動物 である
  犬 は、動物 である
は、成り立ちますが、
  動物 は、猫 である  と  動物 は、犬 である  は、正確ではありません。

ここで、
  Catクラス を、Animalクラス を、継承して、作りました。
  Dogクラス を、Animalクラス を、継承して、作りました。
この時、
  Catクラス is Animalクラス は、true/正しい です。
  Dogクラス is Animalクラス は、true/正しい です。
これは、こういうルール になっています
また、
  Animalクラス is Catクラス ➡ false/間違い
  Animalクラス is Dogクラス ➡ false/間違い
です。

これを踏まえて、
  27~34行目:main()関数:
    (関数) fun theRemainingFuel(c: Car): Double を、定義。
      / これは、Car型 であれば、DeluxeCar型 でも、Jeep型 でも
      / 残ガソリン量 を、計算できる 関数 です。
        / ここで、多態性 が、使われています。
        / Car型 であれば、実際には、DeluxeCar型 でも、Jeep型 でも、
        / 多様に対応できるという事です。
          / 引数は、「c: Car」 つまり、Car型 の、インスタンス.
            / carインスタンス is Car型
            / deluxeCarインスタンス is Car型
            / jeepインスタンス is Car型

    ロジックは、when式 を使って、
      is DeluxeCar
        true -> deluxeCar.theRest(40.0/走った距離)
      is Jeep
        true -> jeep.theRest(40.0)
      else
        true -> car.theRest(40.0)
          / deluxeCar でも、jeep でもないのは、carインスタンス だけ.

    when式 が、返すのは、theRest()メソッド の結果、、残ガソリン量:Double です。
    それを、変数trFuel に、代入して、
    trFuel を、return します。

準備が整いました:
  36~38行目:main()関数:
    36行目:theRemainingFuel(Car型インスタンスの car) が、返したものを、出力。
      / 最初に、「文字列テンプレート」 の中に、返します。
    37行目:theRemainingFuel(Car型インスタンスの deluxeCar) が、返したものを、出力。
    38行目:theRemainingFuel(Car型インスタンスの jeep) が、返したものを、出力。
      / deluxeCarインスタンス は、override された メソッド を、使います.
      / jeepインスタンス は、override された メソッド を、使います.

結論:
  多態性を、使って、多様な Car型インスタンス を、1つのメソッドで、対応しました。



まとめ
length は、Stringクラス の、プロパティ で、
 Stringインスタンス.length で、文字列の長さを、返します。
is演算子 の使い方
・ 子供クラス is 親クラス である。(その逆は、成り立たない)
多態性 を使えば、継承関係のある多様なインスタンス を、引数にして、
 1つのメソッドで、対応できる。