6. Сравните абстрактные классы и интерфейсы
Иногда возникает необходимость определить общее поведение или свойства, которые будут использоваться всеми связанными классами. Kotlin предлагает два способа сделать это: интерфейсы и абстрактные классы. В этом задании вы создадите абстрактный AquariumFishкласс для свойств, общих для всех рыб. Вы создадите интерфейс FishAction, определяющий поведение, общее для всех рыб.
- Ни абстрактный класс, ни интерфейс не могут быть созданы как экземпляры, абстрактные классы могут иметь конструкторы.
- Поскольку интерфейсы не являются классами, они не могут иметь никакой логики конструктора.
- Интерфейсы не могут хранить какое-либо состояние.
Шаг 1. Создайте абстрактный класс.
- В папке example.myapp создайте новый файл
AquariumFish.kt. - Создайте класс, также называемый
AquariumFish, и пометьте его какabstract. - Добавьте одно
Stringсвойствоcolorи отметьте его какabstract.
package example.myapp
abstract class AquariumFish {
abstract val color: String
}
- Создайте два подкласса
AquariumFish,SharkиPlecostomus. - Поскольку
colorэто абстрактная концепция, подклассы должны её реализовывать. СделайтеSharkсерый иPlecostomusзолотой цвета.
class Shark: AquariumFish() {
override val color = "grey"
}
class Plecostomus: AquariumFish() {
override val color = "gold"
}
- В файле Main.kt создайте
makeFish()функцию для тестирования ваших классов. Создайте экземпляры классов aSharkи aPlecostomus, а затем выведите цвет каждого из них. - Удалите свой предыдущий тестовый код
main()и добавьте вызов функцииmakeFish(). Ваш код должен выглядеть примерно так, как показано ниже.
Main.kt:
package example.myapp
fun makeFish() {
val shark = Shark()
val pleco = Plecostomus()
println("Shark: ${shark.color}")
println("Plecostomus: ${pleco.color}")
}
fun main () {
makeFish()
}
- Запустите свою программу и посмотрите на результат.
⇒ Shark: grey Plecostomus: gold
Следующая диаграмма отображает иерархию классов нашего приложения. И Sharkкласс, и Plecostomusподкласс являются абстрактным классом AquariumFish.

Шаг 2. Создайте интерфейс.
- В файле AquariumFish.kt создайте интерфейс
FishActionс методомeat().
interface FishAction {
fun eat()
}
- Добавьте это
FishActionв каждый из подклассов и реализуйтеeat()это, заставив его выводить на экран то, что делает рыба.
class Shark: AquariumFish(), FishAction {
override val color = "grey"
override fun eat() {
println("hunt and eat fish")
}
}
class Plecostomus: AquariumFish(), FishAction {
override val color = "gold"
override fun eat() {
println("eat algae")
}
}
- В
makeFish()функции в файле Main.kt заставьте каждую созданную вами рыбу съесть что-нибудь, вызвав методeat().
fun makeFish() {
val shark = Shark()
val pleco = Plecostomus()
println("Shark: ${shark.color}")
shark.eat()
println("Plecostomus: ${pleco.color}")
pleco.eat()
}
- Запустите свою программу и посмотрите на результат.
⇒ Shark: grey hunt and eat fish Plecostomus: gold eat algae
На следующей диаграмме представлены Sharkкласс и Plecostomusкласс, оба из которых реализуют FishActionинтерфейс.

Когда следует использовать абстрактные классы, а когда — интерфейсы?
Приведенные выше примеры просты, но когда у вас много взаимосвязанных классов, абстрактные классы и интерфейсы могут помочь сделать вашу архитектуру чище, организованнее и проще в обслуживании.
Как отмечалось выше, абстрактные классы могут иметь конструкторы, а интерфейсы — нет, но в остальном они очень похожи. Итак, когда следует использовать каждый из них?
При использовании интерфейсов для проектирования класса его функциональность расширяется за счет методов, реализованных в этих интерфейсах. Использование трейтов, определенных в интерфейсах, как правило, упрощает повторное использование и понимание кода по сравнению с наследованием от абстрактного класса. Кроме того, в классе можно реализовать несколько интерфейсов, но наследовать можно только от одного класса. Общее правило – по возможности отдавать предпочтение композиции (т.е. интерфейсам и ссылкам на экземпляры) перед наследованием.
- Используйте абстрактный класс всякий раз, когда вы не можете завершить создание класса. Например, вернувшись к
AquariumFishклассу, вы можете сделать так, чтобы все классы реализовывалиAquariumFishинтерфейс `<div>`FishAction, и предоставить реализацию по умолчанию для `<div>`,eatоставаясь при этомcolorабстрактным, поскольку для рыб на самом деле нет цвета по умолчанию.
interface FishAction {
fun eat()
}
abstract class AquariumFish : FishAction {
abstract val color: String
override fun eat() = println("yum")
}