メソッド
Kotlinでは、オーバーライド可能なメンバーには明示的に「open」キーワードを付けなければいけません。
また、子クラスで実際にオーバーライドしているメンバーには「override」キーワードを付ける必要があります。
fun main(args: Array) {
val student = Student("suzuki", 82.1)
student.describe()
}
open class Person(name: String) {
val name = name
open fun describe() {
println("Name is ${name}.")
}
}
class Student(name: String, score: Double) : Person(name) {
val score = score
override fun describe() {
println("Name is ${name}. Score is ${score}.")
}
}
Name is suzuki. Score is 82.1.
オーバーライドしたメンバー自身は、自動的に「open」になります。
(上記の例で言えば、Studentクラスのdescribe関数は、Studentの子クラスでオーバーライド可能です)
もし、これ以上、子クラスでオーバーライドしてほしくなければ、「final」キーワードを付けます。
そうすることで、それ以上のオーバーライドを禁じることができます。
fun main(args: Array) {
val student = Student("suzuki", 82.1)
student.describe()
}
open class Person(name: String) {
val name = name
open fun describe() {
println("Name is ${name}.")
}
}
class Student(name: String, score: Double) : Person(name) {
val score = score
// finalをつけるとこれ以上、子クラスでオーバーライドできなくなる
final override fun describe() {
println("Name is ${name}. Score is ${score}.")
}
}
プロパティ
プロパティもメソッドと同様にオーバーライドすることができます。
プロパティのオーバーライドでは、親クラスで読取り専用の「val」だったものも、子クラスで「var」にオーバーライドスルことができます。
これは、getterしかなかったプロパティにオーバーライドによってsetterを追加することが可能だということです。
fun main(args: Array) {
val student = Student("suzuki", 82.1)
student.name = ""
student.describe()
}
open class Person(name: String) {
// openをつけることでオーバーライド可能にする
open val name = name
open fun describe() {
println("Name is ${name}.")
}
}
class Student(name: String, score: Double) : Person(name) {
override var name = name
set(value) {
if (value.isEmpty()) {
field = "empty"
} else {
field = value
}
}
val score = score
// finalをつけるとこれ以上、子クラスでオーバーライドできなくなる
final override fun describe() {
println("Name is ${name}. Score is ${score}.")
}
}
Name is empty. Score is 82.1.
継承時の初期化の順序
子クラスをインスタンス化した時の、初期化の順番は次のとおりになります。
- 親クラスのコンストラクタに渡す引数の評価
- 親クラスの記述されている初期化処理
- 子クラスに記述されている初期化処理
fun main(args: Array) {
val student = Student("suzuki", 82.1)
}
open class Person(name: String) {
init {
println("Base: init block")
}
val name = name.also { println("Base: property initializer. ${it}") }
}
class Student(name: String, score: Double) : Person(name.also { println("Derived: Argument for Base Constructor") }) {
init {
println("Derived: init block")
}
var score = score.also { println("Derived: property initializer. ${it}") }
}
Derived: Argument for Base Constructor
Base: init block
Base: property initializer. suzuki
Derived: init block
Derived: property initializer. 82.1
super
子クラスで親クラスのメンバーにアクセスして処理を行ったり、値を設定したりしたい場合があります。
そのような場合には、「super」を使って親クラスのメンバーにアクセスします。
fun main(args: Array) {
val student = Student("suzuki", 82.1)
student.name = ""
student.describe()
}
open class Person(name: String) {
open val name = name
open fun describe() {
println("Name is ${name}.")
}
}
class Student(name: String, score: Double) : Person(name) {
override var name = name
set(value) {
if (value.isEmpty()) {
field = "empty"
} else {
field = value
}
}
val score = score
final override fun describe() {
// superを使って親クラスのメンバーを呼び出す
super.describe()
println("Name is ${name}. Score is ${score}.")
}
}
Name is empty.
Name is empty. Score is 82.1.
複数の実装をもつメンバーのオーバーライドルール
クラスやインターフェースを複数継承した場合に、それぞれの親クラスやインターフェースに
同じ名前のメンバーが別々の実装を持っている場合があります。
このような場合には、子クラスでは必ずそのメンバーをオーバーライドして子クラス独自に処理を実装する必要があります。
(superを使っていずれかの親クラスやインターフェースの処理を採用しても、もちろんOKです)
複数の親クラスやインターフェースを継承した場合は、「super<base>」とすることでそれぞれの親クラスやインターフェースにアクセスします。
fun main(args: Array) {
val student = Student("suzuki", 82.1)
student.describe()
}
open class Person(name: String) {
val name = name
open fun describe() {
println("Name is ${name}.")
}
}
interface Describable {
fun describe() {
println("dummy")
}
}
class Student(name: String, score: Double) : Person(name), Describable {
val score = score
// describeの実装が複数存在してしまうため、
// オーバーライドして独自に実装しなおさないとビルドエラーになる
override fun describe() {
// それぞれのdescribeにアクセスできる
super.describe()
super.describe()
println("Name is ${name}. Score is ${score}.")
}
}
Name is suzuki.
dummy
Name is suzuki. Score is 82.1.