Прямые ссылки на публичные уроки для быстрого старта и стабильной индексации lesson-страниц.
Абстрактный класс предназначен только для описания общих данных — создавать его экземпляры нельзя. От него можно наследоваться, при этом ключевое слово open уже не требуется: оно подразумевается по умолчанию.
Абстрактные классы могут содержать абстрактные свойства и методы. Абстрактные свойства не инициализированы, абстрактные методы не имеют тела. Все они обязательны к реализации в каждом классе-наследнике — IDE подсветит ошибку при их отсутствии и предложит либо добавить реализацию, либо объявить наследника тоже абстрактным. Реализация выполняется с ключевым словом override.
abstract class SpaceShuttle { abstract val tankSize: Int abstract fun runDiagnostics() } class Upsilon(override val tankSize: Int) : SpaceShuttle() { override fun runDiagnostics() { println("Диагностика запущена") } }
Интерфейс описывает часть функциональности класса, сгруппированную по смыслу. Создавать объекты на его основе нельзя, зато один класс может реализовывать несколько интерфейсов — это называют множественным наследованием (в отличие от классов, от которых можно наследоваться только от одного). Интерфейсы удобно использовать там, где нужна лишь часть возможностей объекта.
Для объявления интерфейса используется ключевое слово interface. Создадим интерфейс Movable, описывающий функциональность движения космического шаттла.
interface Movable { }
В интерфейсе можно объявить методы с реализацией по умолчанию или без неё. Метод startEngine() получит реализацию по умолчанию — одинаковую для всех шаттлов. Методы prepareForTakeoff() и prepareForLanding() объявляются без тела, поскольку у каждого типа корабля подготовка будет специализированной.
interface Movable { fun startEngine() { println("Двигатель запущен") } fun prepareForTakeoff() fun prepareForLanding() }
По аналогии создадим интерфейс Shootable, отвечающий за стрельбу из орудий.
interface Shootable { fun startShooting() fun reloadGuns() }
В интерфейсах ключевое слово abstract для методов не нужно — все методы без тела абстрактные по умолчанию.
Интерфейсы добавляются к классу так же, как наследование — через двоеточие, но без скобок. Несколько интерфейсов перечисляются через запятую.
abstract class SpaceShuttle : Movable, Shootable { abstract val tankSize: Int abstract fun runDiagnostics() }
В классе-потомке Upsilon необходимо реализовать все абстрактные методы из унаследованных интерфейсов. Метод startEngine() реализовывать не обязательно — у него уже есть реализация по умолчанию. При необходимости его также можно переопределить.
Шаттл Upsilon отличается изменяемой геометрией крыльев, что отразим в методах взлёта и посадки.
class Upsilon(override val tankSize: Int) : SpaceShuttle() { override fun runDiagnostics() { println("Диагностика запущена") } override fun prepareForTakeoff() { println("Развернуть крылья") } override fun prepareForLanding() { println("Свернуть крылья") } override fun startShooting() { println("Начать стрельбу") } override fun reloadGuns() { println("Перезарядить орудия") } }
Применяя интерфейсы к базовому классу, а не к каждому наследнику отдельно, мы избегаем дублирования кода — обязанность реализовать методы автоматически распространяется на всех потомков SpaceShuttle.
При запуске программы все методы экземпляра Upsilon отрабатывают корректно.