Menu
Coddy logo textTech

メンバーのオーバーライド

CoddyのKotlinジャーニー「オブジェクト指向プログラミング」セクションの一部。レッスン 18/57。

サブクラスは、継承したメソッドを独自のバージョンに置き換えることができます。スーパークラスはメソッドに open を付け、サブクラスは override を付けてそのメソッドを再び宣言します。

open class Animal(val name: String) {
    open fun sound() = "..."
}
class Dog(name: String) : Animal(name) {
    override fun sound() = "Woof"
}
class Cat(name: String) : Animal(name) {
    override fun sound() = "Meow"
}

main の中では次のようにします。

println(Dog("Rex").sound())
println(Cat("Tom").sound())
println(Animal("Generic").sound())

出力:

Woof
Meow
...

実行されるバージョンは、変数の型がスーパークラスであっても、オブジェクトの実際のクラスによって選択されます。オープンメソッドを呼び出すスーパークラス内のコードも、サブクラスのバージョンを取得します。

open class Animal(val name: String) {
    open fun sound() = "..."
    fun speak() = "$name says ${sound()}"
}
class Dog(name: String) : Animal(name) {
    override fun sound() = "Woof"
}

mainの中では:

val pets: List<Animal> = listOf(Dog("Rex"), Animal("Generic"))
for (p in pets) println(p.speak())

出力:

Rex says Woof
Generic says ...

プロパティも同じ方法でオーバーライドできます。スーパークラスでは open val、サブクラスでは override val を使用し、通常のプロパティ、コンストラクタプロパティ、または計算ゲッターとして定義できます。

open class Shape {
    open val name = "shape"
    open val corners = 0
}
class Triangle : Shape() {
    override val name = "triangle"
    override val corners = 3
}
class Polygon(override val corners: Int) : Shape() {
    override val name: String
        get() = "$corners-gon"
}

main 内では次のようにします。

for (s in listOf(Shape(), Triangle(), Polygon(6))) println("${s.name}: ${s.corners}")

出力:

shape: 0
triangle: 3
6-gon: 6

override としてマークされたメンバーは再びオープンになるため、サブクラスのサブクラスであればもう一度オーバーライドできます。final override はそれを停止します。open のないメンバーはまったくオーバーライドできず、サブクラスで同じシグネチャを持つメンバーを宣言することもできません。

open class Animal {
    open fun sound() = "..."
    fun sleep() = "zzz"
}
open class Dog : Animal() {
    final override fun sound() = "Woof"
}
class Puppy : Dog() {
    // override fun sound() = "Yip"   // エラー: 'Dog' の 'sound' は final です
    // fun sleep() = "nap"            // エラー: 'sleep' はスーパークラスのメンバーを隠蔽します
}
challenge icon

チャレンジ

簡単

Animal には legs()(4)、sound()(...)、および describe() があり、legs() と sound() から構成された Rex has 4 legs and says Woof を返します。legs() と sound() をオーバーライド可能にし、その2つのメソッドだけをオーバーライドしてサブクラスを完成させてください。Dog は Woof と鳴き、Bird は脚が2本で Tweet と鳴き、Snake は脚が0本で Hiss と鳴きます。describe() は Animal にあるままにします。

提供されたコードは、dog,Rex、bird,Pip、snake,Kaa、またはその他の種類(通常の Animal)の行を読み取り、動物を1つの List<Animal> に格納し、それぞれに対して describe() を出力します。

コードは Animal.kt、Dog.kt、Bird.kt、Snake.kt に記述します。Main.kt には提供された入出力コードが含まれており、編集できません。

自分で試してみよう

fun main() {
    // 提供された入出力コード: そのままにしておく
    val input = generateSequence(::readLine).toList()
    val animals = mutableListOf<Animal>()
    for (line in input) {
        val (kind, name) = line.split(",")
        animals.add(when (kind) {
            "dog" -> Dog(name)
            "bird" -> Bird(name)
            "snake" -> Snake(name)
            else -> Animal(name)
        })
    }
    for (a in animals) println(a.describe())
}
quiz icon腕試し

このレッスンには短いクイズがあります。レッスンを始めて解答し、進捗を記録しましょう。

オブジェクト指向プログラミングのすべてのレッスン

自分で練習してみよう: Kotlinプレイグラウンド