Урок 15: Абстрактные классы и интерфейсы в Kotlin. Имплементация

Урок 15: Абстрактные классы и интерфейсы в Kotlin. Имплементация

Абстрактные классы

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

Свойства и методы абстрактных классов

Покажу на примере нового класса SpaceShuttle. Абстрактные классы также могут иметь абстрактные свойства и методы. Свойства в таком случае не проинициализированны, а методы не имеют тела. Добавим такие общие данные, как размер топливного бака и метод запуска диагностики. Тут же создадим класс-потомок с названием одного из шаттлов, использовавшимся Первым орденом, Upsilon. И здесь нам уже необходимо реализовать все абстрактные свойства и методы, среда разработки подсветит ошибку и предложит исправление в случае отсутствия. Либо предложит сделать этот класс также абстрактным. Переопределяем все, что нужно, сопровождая ключевыми словами override.

abstract class SpaceShuttle {

   abstract val tankSize: Int
   abstract fun runDiagnostics()

}

class Upsilon(override val tankSize: Int) : SpaceShuttle() {
   override fun runDiagnostics() {
	  println("Диагностика запущена")
   }

}

В каких случаях используют интерфейсы

Интерфейсы в свою очередь позволяют описать некоторую часть внешнего представления наследуемого, а точнее сказать – имплементирующего их класса. Таким образом интерфейс можно использовать в тех частях приложения, где требуется только часть особенностей создаваемого экземпляра. Создавать на его основе объекты нельзя. В основном интерфейсы описывают функционал, как правило, сгруппированный по смыслу. Этот функционал можно включать во множество классов. Преимущество интерфейсов в том, что для класса можно реализовать несколько интерфейсов. Все они перечисляются через запятую. Назовем это множественное наследование. А класс можно отнаследовать только от одного класса-родителя.

Функционал интерфейсов

Создадим интерфейс в отдельном файле, в пакете текущего урока. Создать интерфейс можно с помощью среды разработки, выбрав при создании Interface. Но создадим отдельный файл и пропишем все руками самостоятельно. Чтобы объявить интерфейс используется ключевое слово interface, затем название интерфейса. Нужно выделить гипотетическую функциональность, которая будет сгруппирована по смыслу. Пусть методы внутри отвечают за движение космического шаттла. Поэтому название будет Movable.

interface Movable {

}

Объявление методов в интерфейсах

В интерфейсе есть возможность объявить методы без реализации – что-то вроде шаблонов или объявить какую-либо реализацию по умолчанию. Для этого просто нужно написать метод с телом. Объявим несколько функций. Предположим, что функция startEngine() будет с одинаковой реализацией по умолчанию для всех шаттлов. Далее создадим абстрактный метод подготовки к взлету prepareForTakeoff() –метод будет без конкретной реализации, потому что для каждого типа корабля подготовка будет специализированная. Точно также, как и метод для посадки prepareForLanding().

interface Movable {

   fun startEngine() {
	   println("Двигатель запущен")
   }

   fun prepareForTakeoff()
   fun prepareForLanding()

}

Так как я упомянул, что интерфейсов может быть множество, создадим по аналогии еще один. Например, Shootable. Он будет отвечать за стрельбу из орудий разного типа, установленных на космическом судне. Абстрактные методы будут простыми –начать стрельбу и перезарядить орудия. В классах, куда этот интерфейс будет имплементирован, будет конкретная специализированная реализация. Из чего стрелять и что перезаряжать.

interface Shootable {

   fun startShooting()
   fun reloadGuns()

}

Итак, мы создали интерфейсы с набором функций, которые будем использовать для различных типов космических шаттлов. Все действия, объявленные внутри будут использоваться для всех объектов. Большая часть их абстрактные, то есть без реализации. Напомню, ключевое слово abstract использовать не нужно, в интерфейсах все методы абстрактные по умолчанию.

Реализация абстрактных методов в интерфейсах

Теперь добавим реализацию интерфейсов к нашему базовому классу SpaceShuttle. Делается это также, как наследование – пишем название интерфейсов через двоеточие, но без использования скобок. Перечисляются они через запятую. В классе потомке (Upsilon) среда разработки сразу подсвечивает ошибку. Необходимо имплементировать (или реализовать) абстрактные методы. Применяем исправление и выбираем все предлагаемые методы. Шаттл Upsilon, как известно, кардинально отличается строением крыльев, которые изменяют свое положение. Поэтому пусть в методах подготовки к взлету и посадке будут действия по изменению геометрии крыльев. Метод startEngine() не требует обязательной имплементации, так как у него уже есть реализация по умолчанию в интерфейсе. Но при необходимости также можно его переопределить.

class Upsilon(override val tankSize: Int) : SpaceShuttle() {
   override fun runDiagnostics() {
   println("Диагностика запущена")
   }

   override fun prepareForTakeoff() {
   println("Развернуть крылья")
   }

   override fun prepareForLanding() {
   println("Свернуть крылья")
   }

start S
reload G

}

Что мы получили. Класс-наследник Upsilon, который наследует базовый класс SpaceShuttle. В последнем объявлено одно абстрактное поле и один абстрактный метод. Так как они абстрактные, их необходимо переопределять. Что и было сделано с помощью аннотаций override.

Далее мы создали интерфейсы и применили их к базовому классу. Мы могли бы применить их и к дочерним классам по отдельности, но в данном случае в этом нет смысла, так как необходимость их реализации отобразится на потомках абстрактного класса. Избегаем дублирования кода. Таким образом реализовали необходимые методы, которые были в интерфейсах без реализации.

Проверим работоспособность. В рабочем файле создаем экземпляр класса Upsilon и пробуем вызвать все его реализованные методы. Программа отрабатывает корректно. Все интерфейсы применены к дочернему классу.

Проверьте себя после изучения темы

Бесплатный бот с тестами и практическими заданиями по Kotlin.

  • 22 бесплатных урока
  • Тесты к каждому уроку (всего 220 вопросов)
  • Задачи в рамках практики KotlinSprint (120 задач) и курсовой проект «Телеграм бот для изучения иностранных слов»
Начать обучение

Вам также может быть интересно

Узнать подробнее
Курс AndroidSprint

Глубокое обучение Android разработке с 0 до получения оффера. Только персональная практика с гарантией получения продуктового опыта.

Узнать подробнее
Узнать подробнее
Практикум по Kotlin

Изучение Котлин с 0 для профессиональной разработки. Личный ментор и разбор кода задач через git-flow.

Узнать подробнее
Узнать подробнее
Бесплатные уроки по Kotlin разработке

Самостоятельное освоение базы по языку для дальнейшего развития в Android/back-end разработке или в автотестах.

Узнать подробнее
Узнать подробнее
Onboarding в разработку

Полное обучение Android разработке с нуля до получения оффера. Делаем упор на практику и обратную связь

Узнать подробнее
Узнать подробнее
Обучающий Kotlin телеграм бот (с тестами)

Ваш основной инструмент для изучения основ языка. Бесплатные тесты и практика внутри.

Узнать подробнее
Узнать подробнее
Бесплатные уроки по Android разработке

Самостоятельное обучение разработке Андроид приложений. Понятные видеоуроки с разжеванными примерами.

Узнать подробнее
Узнать подробнее
Курс по UI/Unit тестированию

Для ручных тестировщиков, которые готовы осваивать автотесты с использованием актуального стека технологий. [в разработке]

Узнать подробнее
Узнать подробнее
Обучающий Android телеграм бот (с тестами)

Бесплатные теоретические тесты для самопроверки. А также информер на практических спринтах по Android.

Узнать подробнее