Kotlinにおける継承、オーバーライド、抽象クラス、インタフェースとスマートキャスト:Android Studioで始めるKotlin入門(5)(1/3 ページ)
Android Studio 3.0を使い、最近話題のプログラミング言語「Kotlin」の特徴を解説する連載。今回は、「継承」など、「クラス」周りの機能を扱っていきます。また、継承に関連して「スマートキャスト」機能についても解説します。
Android Studioを使い、Kotlin言語の特徴を解説する本連載「Android Studioで始めるKotlin入門」。連載第5回目の今回は、前回に続き、「継承」など、「クラス」周りの機能を扱っていきます。また、継承に関連して「スマートキャスト」機能についても解説します。
クラスの継承
Kotlinのクラス継承は基本的にJavaの考え方を踏襲していますが、幾つか異なる点も存在します。
基底はObjectクラスではなくAnyクラス
Kotlinのクラスは全て「Any」というクラスを継承しています。Kotlinのクラスは全て非Null型のため、Null許容型であるJavaのObjectのサブクラスとなることはできません。そのため、非Null型として定義されたAnyクラスがKotlinにおける全てのクラスのスーパークラスとなっています。AnyのさらにスーパークラスとしてNull許容型である「Any?」型が存在するという関係になります。
AnyクラスはJavaの「Object」クラスとは別物ですが、JavaのObjectと同じく「toString()」「hashCode()」「equals()」というメソッドを持っています。また相互運用性のため、KotlinからJavaクラスを呼び出す際は、JavaのObjectは自動的に「Any!」型として扱われます。
基本的な継承の書き方
Kotlinでのクラス継承の書き方を見てみましょう(リスト1)。
//基底クラスAnimal。openを付けないと継承できない open class Animal{} //Animalを継承したCatクラス。「extends」ではなく「:」で基底クラスを指定する class Cat: Animal(){}
ポイントは2つで、まずKotlinのクラスはデフォルトでJavaのfinal(継承不可)の扱いとなっているため、基底クラスでは「open」というキーワードを付けて継承を許可しなければなりません。そして、継承するクラスではJavaの「extends」キーワードではなく、「:」(コロン)で基底クラスを指定します。C++やC#っぽい書き方ですね。
基底クラスのコンストラクタ呼び出し
続いて基底クラスのコンストラクタ呼び出しについて、リスト2を見てみましょう。
//基底クラス open class Person(val firstName: String, val lastName: String = ""){……} //継承クラス。継承元のコンストラクタを指定 class BussinessPerson(firstName: String, lastName: String = "", val age: Int) : Person(firstName, lastName){……}
「Person」クラスのプライマリコンストラクタでは「firstName」「lastName」の2つのプロパティが定義されています。それを継承したBussinessPersonクラスのコンストラクタでは、firstName、lastNameの2つの引数に加えてageプロパティを定義しています。そして、firstName、lastName引数については、基底クラスであるPersonクラスのコンストラクタに渡しています。
なお、プライマリコンストラクタが存在せず、セカンダリコンストラクタだけの場合はリスト3のように「super」キーワードを使って基底クラスのコンストラクタを呼び出します。
//プライマリコンストラクタのないクラス class Derived : Base{ //セカンダリコンストラクタはsuperキーワードで直接基底クラスのコンストラクタを呼び出す constructor(a: Any) : super(a){ } //同上 constructor(a: Any, b: Any) : super(a, b){ } }
繰り返しとなりますが、セカンダリコンストラクタから直接基底クラスのコンストラクタを呼び出すのは、そのクラスにプライマリコンストラクタを定義しない場合のみです。プライマリコンストラクタがある場合は、前回解説した通り、セカンダリコンストラクタは最終的に必ずプライマリコンストラクタを呼び出す必要があります。
メソッドのオーバーライド
メソッドのオーバーライドについては、リスト4のようになります。
//基底クラス open class Person(val firstName: String, val lastName: String = ""){ //「open」キーワードでオーバーライド可能に open fun print(){ println("お名前: ${firstName} ${lastName}") } } //継承クラス。継承元のコンストラクタを指定 class BussinessPerson(firstName: String, lastName: String = "", val age: Int) : Person(firstName, lastName){ //メソッドのオーバーライド override fun print(){ println("お名前: ${firstName} ${lastName}, 年齢: $age") } } …… var bussinessPerson = BussinessPerson("Tanaka", "Taro", 22) bussinessPerson.print() //「お名前: Tanaka Taro, 年齢: 22」
Kotlinのメソッドはクラスと同様にデフォルトではオーバーライド不可のため、基底クラスで「open」キーワードを使ってメソッドをオーバーライド可能にしておく必要があります。また、継承先では「override」キーワードで、オーバーライドすることを明示的に宣言しなければなりません。
「デフォルトでクラスもメソッドも継承不可になっている」点がJavaとちょっと違う部分ですが、デフォルトを厳しめにすることで、継承に関わるさまざまなトラブルを避けられるようになっています。例えば、オーバーライドしたつもりがミスタイプしていた、なんてことも避けられますね。
Javaでも最近は@Overrideアノテーションを付けることで、同様のチェックが行えるようになっていますが、Kotlinでは言語レベルでサポートされています*1。
*1)C#なども同様の仕組みを取っています。
『Effective Java』(柴田芳樹 訳、丸善出版)というJavaプログラマー必見の名著がありますが、その「Item 17」では「継承のために設計および文書化する、そうではないなら継承を禁止する」という項目があり、オーバーライド可能なメソッドを絞り込むことなどが提案されています。
Kotlinでは、この原則に従い、意図せず継承可能なクラス、オーバーライド可能なメソッドを作り込んでしまわないよう、デフォルトで継承不可になっているようです。
抽象クラス
抽象クラスと抽象メソッドについてはリスト5のようになります。
//抽象クラス abstract class Vehicle{ //抽象メソッド abstract fun run() } //抽象クラスの具象化 class Car : Vehicle(){ //抽象メソッドのオーバーライド override fun run(){ println("Running!") } } …… //抽象クラスはインスタンス化できない val vehicle = Vehicle() //→コンパイルエラー //継承したクラスのインスタンス化と呼び出し val car = Car() car.run()
オーバーライド時にoverrideキーワードが必要な以外は、Javaとほぼ同様なので、細かな解説は省略します。
Copyright © ITmedia, Inc. All Rights Reserved.
関連記事
- Kotlin(プログラミング言語)
Kotlin(コトリン)は「実用的」であることを主眼においたプログラミング言語だ。その特徴は簡潔/安全/汎用/(Javaとの)相互運用性にある。 - JVMとAndroid用のオープンソースプログラミング言語「Kotlin 1.0」がリリース
JetBrainsが主導して開発を進めるJVMとAndroid用のオープンソースプログラミング言語の正式版がリリース。既存のコードやインフラとの相互運用性を重視した実用的なプログラミング言語だという。 - 2017年プログラミング言語別平均年収ランキング、1位は「Scala」 平均626万円
ビズリーチは、「プログラミング言語別 平均年収ランキング2017」を発表した。第1位はScalaで平均年収は626万円、第2位はPythonで601万円、第3位はKotlinで577万円だった。SwiftやRuby、Javaなどもランクインした。