Menu
Coddy logo textTech

Переопределение членов

Часть раздела Объектно-ориентированное программирование путешествия по Kotlin на Coddy. Урок 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"   // ошибка: 'sound' в 'Dog' является final
    // fun sleep() = "nap"            // ошибка: 'sleep' скрывает член суперкласса
}
challenge icon

Задание

Легко

Animal имеет legs() (4), sound() (...) и describe(), которая возвращает Rex has 4 legs and says Woof, используя legs() и sound(). Сделайте legs() и sound() переопределяемыми и завершите подклассы, переопределив только эти два метода: Dog говорит Woof, у Bird 2 ноги, и она говорит Tweet, а у Snake 0 ног, и она говорит Hiss. describe() остаётся таким, как есть, в Animal.

Предоставленный код считывает строки dog,Rex, bird,Pip, snake,Kaa или любой другой вид (обычный Animal), сохраняет животных в одном 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