Menu
Coddy logo textTech

Открытые классы и подклассы

Часть раздела Объектно-ориентированное программирование путешествия по Kotlin на Coddy. Урок 17 из 57.

Наследование позволяет одному классу строиться на основе другого. Подкласс получает все свойства и методы своего суперкласса и может добавлять собственные. По умолчанию классы Kotlin закрыты, поэтому перед тем, как другой класс сможет расширить класс, его необходимо пометить как open:

open class Animal(val name: String) {
    fun describe() = "$name is an animal"
}

class Dog(name: String) : Animal(name) {
    fun fetch() = "$name fetches the ball"
}

Внутри main:

val rex = Dog("Rex")
println(rex.describe())
println(rex.fetch())

Результат:

Rex is an animal
Rex fetches the ball

class Dog(name: String) : Animal(name) означает, что Dog расширяет Animal и передаёт name конструктору суперкласса. У параметра нет val, потому что свойство уже существует в Animal. Класс, который не является open, нельзя расширить:

class Plant(val name: String)
// class Tree(name: String) : Plant(name)   // error: этот тип final, поэтому его нельзя расширить

open class Vehicle(val wheels: Int)
class Bike : Vehicle(2)                     // передаёт фиксированное значение
class Truck(val load: Int) : Vehicle(6)     // добавляет своё собственное свойство

Dog является Animal, поэтому экземпляр подкласса можно использовать везде, где ожидается тип суперкласса. Один List<Animal> может содержать собак, кошек и обычных животных, и у каждого элемента есть члены Animal:

open class Animal(val name: String) {
    fun describe() = "$name is an animal"
}
class Dog(name: String) : Animal(name)
class Cat(name: String, val indoor: Boolean) : Animal(name)

Внутри main:

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

Вывод:

Rex is an animal
Tom is an animal
Generic is an animal

Наследование моделирует отношение is-a: собака является животным, сберегательный счёт является счётом. Когда один класс лишь использует другой (у автомобиля есть двигатель), вместо этого добавьте ему свойство такого типа. В Kotlin классы по умолчанию закрыты, поэтому класс, предназначенный для расширения, должен явно объявить об этом с помощью open, а каждый другой класс сохраняет своё поведение защищённым от подклассов.

open class Account(val owner: String)
class SavingsAccount(owner: String) : Account(owner)   // is-a: наследование

class Engine(val horsepower: Int)
class Car(val engine: Engine)                          // has-a: свойство
challenge icon

Задание

Легко

Manager повторяет то, что уже есть в Employee. Сделайте Employee открытым классом и превратите Manager в его подкласс: Manager(name: String, salary: Int) передаёт оба значения в Employee и оставляет только новые элементы — приватный список имён подчинённых с помощью addReport(name) и teamSize(). В Employee функция raise(percent) увеличивает зарплату на указанный процент с округлением вниз, а describe() возвращает Ada earns 5500.

Предоставленный код создаёт менеджера из первой строки (name,salary), добавляет каждое последующее имя в качестве подчинённого, повышает зарплату на 10% и выводит Ada earns 5500 and leads 2, а затем проверяет, что менеджер является Employee.

Ваш код должен находиться в Employee.kt и Manager.kt. В Main.kt содержится предоставленный код ввода/вывода, и его нельзя изменять.

Попробуйте сами

fun main() {
    // Предоставленный код ввода/вывода: оставьте его как есть
    val input = generateSequence(::readLine).toList()
    val (name, salary) = input[0].split(",")
    val boss = Manager(name, salary.toInt())
    for (report in input.drop(1)) boss.addReport(report)
    boss.raise(10)
    println("${boss.describe()} and leads ${boss.teamSize()}")
    println("a manager is an employee: ${(boss as Any) is Employee}")
}
quiz iconПроверьте себя

В этом уроке есть небольшой тест. Начните урок, чтобы ответить на вопросы и сохранить прогресс.

Все уроки раздела Объектно-ориентированное программирование

Потренируйтесь самостоятельно: Песочница Kotlin