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

абстрактные классы. Имплементация 1 - Android [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 и пробуем вызвать все его реализованные методы. Программа отрабатывает корректно. Все интерфейсы применены к дочернему классу.

Для тех, кто собрался стать Android-разработчиком

Пошаговаясхема
Пошаговая
схема

Описание процесса обучения от основ Kotlin до Android-разработчика

Бесплатныеуроки
Бесплатные
уроки

Авторский бесплатный курс по основам языка программирования Kotlin

Обучающийбот
Обучающий
бот

Тренажер и самоучитель по Котлин – бесплатные тесты и практика

Поделиться уроком

Ответить

Ваш адрес email не будет опубликован. Обязательные поля помечены *