19.0429

抽象クラス、抽象メソッド、abstractキーワード

抽象クラスとは、そのまま、インスタンス化 するには、未完成です。
つまり、何をするのかは、決まっているのだけど、
それは、具体的な完成品によって、実装/どうやって実現するのか が、違うため、
まだ メソッド の中身が書かれていない、class。
または、まだ何も書かれていないが、外枠だけは出来ている、HTMLコード のテンプレート。
そんなイメージの、class です。

そんなイメージが、ありありと感じられる、class/コード を、書きたかったのですが、
まあ、それに関しては、今一つですが、というより全然そんな感じがしなくて残念ではありますが、
Kotlin文法、、abstractキーワード の使い方・ルール などは、
分かりやすいと思う、こんなコードを、書いてみました


1. 抽象class のルール
抽象クラス は、単に、コードが書かれていない、メソッド (プロパティも、abstractキーワード を、付けられる)
があるだけだと考えれば、普通のclass と、同じように、継承して、子供クラス/サブクラス を、
作っていけるので、それほど特別なクラスでもないです。
ただ、抽象クラス は、テンプレートとしては、よくできていても、そのままインスタンスにするには、
あまり意味がないclass だと言えるでしょう。

そして、インスタンス化できないために、いろいろなルールに縛られているようです。
ここでは、そんなルールをあげていきます。
  1. 抽象メソッド、抽象プロパティ が、あるclassは、abstractキーワード
    を、付けなければいけない。
  2. 中身のないメソッドには、abstractキーワード を、付けなければならない。
    そして、そんな抽象メソッド には、{ } は、たとえ中身が 空でも、付けないこと。
  3. 抽象メソッドを持つ抽象class を、継承したclassは、抽象メソッド を、必ず、実装しなければならない。
   (コードを書かないといけない)要は、インスタンス化 できるclass にする必要がある。
  4. abstractメソッドは、継承した時、必ず、override されます。
    abstractキーワード が、そのことを示しているので、openキーワードを付けても無駄である。
  5. 抽象メソッドを、オーバーライドする時は、必ず、overrideキーワード が、必要です。


2. 抽象class を、使ったコードで、ルールを確認する
それでは、改めて、抽象classを使ったコードを、見てみましょう。
まず、このコードのやっていることですが、
  抽象class Car を、作ります。
  そのCarクラスを、継承した、DeluxeCarクラスJeepクラス を、作ります。
  親クラスであるCarクラス には、abstractメソッド/抽象メソッド
    decideColor(color: String): String(車の色を決めるメソッド)
  が、あります。
  そして、2つの子供クラス、DeluxeCarクラス、Jeepクラス で、
  その抽象メソッド/decideColor() を、実装します。
  最後に、多態性を、利用して、
    returnCarColor(c: Car): String(車の色を返すメソッド)
  を、実行します。

ここでは、特に、abstractキーワード、overrideキーワード の、
使い方に、注目してほしいと思います。

それでは、順番に見ていきます。
  1~3行目:
    Carクラス を、作っていますが、
      abstract fun decideColor(color: String): String
    が、抽象メソッドなので(頭にabstractキーワード、{ }がない)、
      abstract class Car
    と、abstractキーワード を付けて、定義します。

  5~9行目:
      class DeluxeCar : Car()
    と、あるように、Carクラス から、継承しています。
    ここで、注意すべきは、
      class DeluxeCar : Car
        / () がないとエラー
    こんな警告を、IntelliJ が出して来ます:
      This type has a constructor, and thus must be initialized here
     (ここには、constructor を、書かなければなりません、、つまり、初期化してください)
      確かに、Carインスタンス を作るには、例えば、「val c = Car()」 でしたね。
      そういう事です、いままで、理解せずに、継承のコード を、構文通りに書いてきたのですが、
      そういう事です。
        :/継承しますのマーク の後には、
        継承元のclass を、初期化という意味があったのでした!
      また、1行目 の、Carクラス には、あたかも、プライマリコンストラクタ がないように見えますが、
        abstract class Car constructor()
        abstract class Car ()
          / constructor は、省略できる
      省略しないと、このように書きます。
        Kotlin イン アクション より:
          コンストラクタ を、宣言しないクラスでは、何もしないデフォルトコンストラクタが生成されます。
            p.106
    とにかく、継承する時は、親クラスのコンストラクタを書いて、初期化しなければならない
   (先の学習ですが、ただし、interface を、継承する時は、初期化できないのだから、
    ()は、書くことはあり得ない[Kotlin イン アクション より])
    しかし、よく考えたら、abstract class も、初期化できないのだ!
    この謎は、解かなければ!でも、さきに、Androidアプリ を作らなければ。
    ぶっちゃけた話、IntelliJ を使えば、多少の文法に不安があっても、
    ほんとに細かいところまでも指摘してくるので、
    少なくとも、文法的には、正しいコードが書けるでしょう。
    何しろ、文法的にあっていても、こうしたほうがいい、ああした方が言ってくるのだから。
    このコードにしても、コードが、文法的にも、さらに推奨される書き方でないと、✓ を、付けないので、
    今回は、✓が付くまで直そうと思って、when式 が返す値を、
    いったん変数に代入せず、直接返すようにしたら、やっと、
    ✓を、付けてくれました。
    でも、最初のころには、省略とかはやらない方が、いいと思うので
   (例えば、コンストラクタを省略せずに書くとか)、
    必ずしも、✓ に、こだわらない方がいいとも言えます。
    何の話だったのだ?そうです、文法の話ですが、
    多分、「Kotlin イン アクション」 が、この謎を解くのに一番よいのでは?
    と思っていますが。abstract class なのに、初期化できないはずなのに、
    どうして、「: クラス名()」 を書くのか?
が、分かったら、
    どこかに、アップロードするつもりです。


    実は、ここが一番のポイントですが、
      decideColor(color: String): String を、override しながら、実装しています
      どのように実装しているかは、単に、引数で受け取った、Stringインスタンス の、color を、
      returnしているだけです。

  11~15行目:
    ここでも、Carクラス を、継承して、
      class Jeep : Car()
    で、Jeepクラス を、定義しています。
    また、ここでも、decideColor()メソッド を、override しています。

  18~19行目:main()関数:
    DeluxeCarインスタンス、deluxeCar を、作成。
    Jeepインスタンス、jeep を、作成

  21~26行目:main()関数:
    インスタンスdeluxeCar も、jeep も、Car型 です。
    親クラスが、abstractクラス であっても、そうです。
    それで、関数の、
      returnCarColor(c: Car): String
    において、Car型 の、引数として受け取り、
    しかし、when式 で、DeluxeCar型であるかそうでないか を、決定して、
    多態性を使って、deluxeCarインスタンス と、jeepインスタンス が持つ、メソッド を、呼び出します。
    ここでは、when式 を、かっこ/() で、くくって、その返す値を、直接、return しています。

  28,29行目:
    ここでは、returnCarColor()メソッド で、得られた値を、
    いったん、文字列テンプレート に、返してから、コンソールに、ほかの文字列と、
    いっしょに、出力しています。



まとめ
抽象クラス と 継承 における、ルール
  1. 抽象メソッド、抽象プロパティ が、あるclassは、abstractキーワード
    を、付けなければいけない。
  2. 中身のないメソッドには、abstractキーワード を、付けなければならない。
    そして、そんな抽象メソッド には、{ } は、たとえ中身が 空でも、付けないこと。
  3. 抽象メソッドを持つ抽象class を、継承したclassは、抽象メソッド を、必ず、実装しなければならない。
   (コードを書かないといけない)要は、インスタンス化 できるclass にする必要がある。
  4. abstractメソッドは、継承した時、必ず、override されます。
    abstractキーワード が、そのことを示しているので、openキーワードを付けても無駄である。
  5. 抽象メソッドを、オーバーライドする時は、必ず、overrideキーワード が、必要です。