19.0428

継承(Inheritance)

継承 とは、
  あるクラスを、
  再利用 && 新しいプロパティ や 新しいメソッド
    / &&(かつ)
または、
  一部を再利用 && 上書きしたプロパティ や 上書きしたメソッド
のようにして、使う方法です。

class と class を、継承関係 で、結び付けると、
  class の、概要/イメージ/要はどんなクラスなのか が、分かりやすくなる
  最小の労力で、今までにないclass を、定義できる
という、利点があります。

よく、遺伝モデルとして、説明されますが、
  子供クラス は、
  親クラス の、プロパティ(まあ外見みたいなもの)、メソッド(まあ能力みたいなもの)
  を、受け継ぎ
    かつ
  新しいプロパティ/メソッド や 今風に合わせたプロパティ/メソッド
  をも、持っている
まあ、おおざっぱに、こんなイメージです。

ここでは、継承 するコード を、見てみます。
ただ、
  プロパティ・メソッド の、書き換え や 追加
に関しては、次回の、「override と 追加」 で、見ていきます。


1. classを継承する: これだけでは、ただの、クローンclass です
では、「継承を生かしたclass」 を、作成する前に、ただの、クローンclass を、作って見ます
右のコードで、継承元/親クラス にあたるのが、
  2行目で、定義している、
  Carクラス
です。
そして、継承先/子供クラス にあたるのが、
  18行目で、定義している
  DeluxeCarクラス(Carクラスよりは、高級車という意味で)
です。

それでは、
  何が、継承されているのか?
を、確認していきましょう。

まずは、
  継承されている プロパティ
から、見ていきます。

まず、
  2行目 Carクラス の、
  コンストラクタ において
  val maxGasoline: Double
というコードで、maxGasoline(車に入る最大ガソリン量という意味で)を、
Double型 で、定義しています。
ただ、ここでは、値 を、設定していません。
つまり、「インスタンス化時に、必ず、初期値を設定する!」 という事でしたね。
また、val で、定義しているという事は、
あるインスタンス 対して、一度、最大ガソリン量 を、定義したら、
新たな値 には、変更できないことを、意味しています。
まあ、これは、普通そうすべきでしょう。
なぜなら、ある車に、新たなガソリン予備タンク を、追加するなどという事は、
普通はないと思いますから。

そして、ここが肝心なのですが、
どんなコードで、「継承」 を、実現しているのかを見てみます。

一番最初に、
  class が、継承できるようにするには、
  継承元のclass に、
  openキーワード
  を、付ける必要がある
のです。
  2行目のコード
  open class Car(val maxGasoline: Double)
  のように、最初に、openキーワード が、付いているのが確認できます。

そして、継承先のclass では、
  18行目 こんな風に、定義
しています。
  class DeluxeCar(maxGasoline: Double) : Car(maxGasoline)
まず、
  :
を使って、
  継承
を、定義します、、Carクラス を、継承したのが、DeluxeCarクラス という流れです。

今度は、引数 に、注目してみましょう。
まず、
  DeluxeCarクラス の、コンストラクタ
は、
  (maxGasoline: Double)
となっています。
ここでは、
  var や val が、ありません。
それは、
  Carクラス の、コンストラクタ で、繰り返しますが、
    val maxGasoline: Double
  と、なっていました。
ということは、
  DeluxeCarクラス においては、
  単に、Carクラス の、定義を、引き継いでいるだけ

ですね。新たに定義しているわけではありませんね。
また、Carクラス においては、
  (maxGasoline)
となっているのは、
  DeluxeCarクラス の、コンストラクタで引き継いだmaxGasoline を、
  単に、Carクラスのコンストラクタに渡す
だけだという事です。
(はじめてのAndroidプログラミング 第4版 (2019/4/20 金田 浩明 著)
 で、得た知識です)
深くは理解できないと思いますが(私もですが)、
この理屈を覚えておくことは、コードを書く上で重要です。
例えば、
  DeluxeCarクラス の、コンストラクタで、
  新たに、プロパティ を、定義する場合は、
  val/var を、使いますから。


今度は、
  メソッド の、継承
について、見てみます。

Carクラス には、
  showMaxGasoline()
  theRest()

という、2つのメソッドがありますが、2つとも、引き継がれています。
  ただし、ここでは、単に、引き継がれているだけ
  です。

最初の、
  showMaxGasoline()
に、関しては、そのまま使っても、問題ありません。
右のコードの、
  25,26行目 で、
  DeluxeCarクラス の、インスタンスdCar を、作成して、
  dCar.showMaxGasoline()
で、呼び出しています。

また、
  theRest()
に関しても、
このメソッドも、同じように、引き継がれています。
ただ、
  10行目 で、
  open fun theRest(drivenKilometers: Double): Double
と、なっていますが、、これには、理由があります。
  DeluxeCarクラス では、燃費が落ちるので、
  theRest()メソッド を、上書きして、書き替えなけれならない
という理由から、
  openキーワード
を、使って、継承先のコード で、上書き出来るように/override出来るように
しておくのです。

override(オーバーライド)に関しては、次回学習します。

もし、ここで、
  27行目に、
  theRest()メソッド を、上書きしないで、
    println(dCar.theRest(20.0))
のようなコードを書いたら、
  Carクラス と、同じ燃費計算で、
  残りの燃料量 を、計算してしまいます。
  DeluxeCarクラスのインスタンス で、20.0km 走った場合は、
  燃費が違うまま計算しているので、
  実際の、残燃料量/残ガソリン量 よりも、多い答えを出してしまう
でしょう。
ですから、ここでは、呼び出していません。
次回、overrideキーワード を使って、書き替えます。

今の状態では、DeluxeCarクラス は、
  ただの、Carクラスのクローン
ですが、
  クローンclassを、土台に、子供クラスを、作り直す
ので、ここは、重要なスタート地点ですね。


最後に、補足をしておきます。
  open class ⇔ final class
    / 継承できるクラス ⇔ 継承出来ないクラス
ただし、finalキーワード は、普通省略します。



まとめ
・ 継承する
   open class 親クラス名(コンストラクタ)
   class 子供クラス名( 引き継いだコンストラクタval/varは不要, [任意:val/var 新たな変数: 型] )
    : 親クラス名( 変数名のみ )
・ 親class を、継承だけした場合、その、子供class は、親クラスクローン と、言える。