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キーワード が、必要です。