Чем отличается абстрактный класс от интерфейса 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("Диагностика запущена") >>

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

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

Читайте также:  Java создание класса наследника

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

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

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

interface Movable < fun startEngine() < println("Двигатель запущен") >fun prepareForTakeoff() fun prepareForLanding() >

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

Итак, мы создали интерфейсы с набором функций, которые будем использовать для различных типов космических шаттлов. Все действия, объявленные внутри будут использоваться для всех объектов. Большая часть их абстрактные, то есть без реализации. Напомню, ключевое слово 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

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

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

Источник

Abstract class vs interface in Kotlin

“What is the difference between abstract class and interface?” — this is one of the most popular questions during programmer recruiting process. Popular answers are:

  • An interface cannot hold state
  • Classes can have actual functions
  • We can implement multiple interfaces and only one class

I will show that those answers are not really true. Interfaces can have properties and can hold state, but not using fields. They can have functions with actual bodies, as long as they are not final. True significant differences between abstract classes and interfaces are:

  • Interfaces cannot have fields
  • We can extend only one class, and implement multiple interfaces
  • Classes have constructors

Interfaces can have functions

In Kotlin interfaces can have functions with default bodies:

Interfaces can have properties

Property in Kotlin represents an abstraction of getter ( val ) or getter and setter ( var ). By default, they have fields used under the hood. This is why this class:

Interfaces formally can have state

It is a bit of a hacking and generally a bad practice, but I just want to show that it is possible to hold state in an interface. We just need to use default bodies to store it. Where can we store it? There are some options and none of them is good. They will all be badly managed and not cleaned properly by the garbage collector. Choosing lesser evil, I decided to store it in a companion object property:

Differences

Abstract classes can have everything that interfaces can, and additionally, they can have fields and constructors. Therefore we can properly hold state in abstract classes:

Источник

Оцените статью