6. Сравните абстрактные классы и интерфейсы

Иногда возникает необходимость определить общее поведение или свойства, которые будут использоваться всеми связанными классами. Kotlin предлагает два способа сделать это: интерфейсы и абстрактные классы. В этом задании вы создадите абстрактный AquariumFishкласс для свойств, общих для всех рыб. Вы создадите интерфейс FishAction, определяющий поведение, общее для всех рыб.

  • Ни абстрактный класс, ни интерфейс не могут быть созданы как экземпляры, абстрактные классы могут иметь конструкторы.
  • Поскольку интерфейсы не являются классами, они не могут иметь никакой логики конструктора.
  • Интерфейсы не могут хранить какое-либо состояние.

Шаг 1. Создайте абстрактный класс.

  1. В папке example.myapp создайте новый файл AquariumFish.kt.
  2. Создайте класс, также называемый AquariumFish, и пометьте его как abstract.
  3. Добавьте одно Stringсвойство colorи отметьте его как abstract.
package example.myapp

abstract class AquariumFish {
    abstract val color: String
}
  1. Создайте два подкласса AquariumFish, Sharkи Plecostomus.
  2. Поскольку colorэто абстрактная концепция, подклассы должны её реализовывать. Сделайте Sharkсерый и Plecostomusзолотой цвета.
class Shark: AquariumFish() {
    override val color = "grey"
}

class Plecostomus: AquariumFish() {
    override val color = "gold"
}
  1. В файле Main.kt создайте makeFish()функцию для тестирования ваших классов. Создайте экземпляры классов a Sharkи a Plecostomus, а затем выведите цвет каждого из них.
  2. Удалите свой предыдущий тестовый код 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()
}
  1. Запустите свою программу и посмотрите на результат.
⇒ Shark: grey 
Plecostomus: gold

Следующая диаграмма отображает иерархию классов нашего приложения. И Sharkкласс, и Plecostomusподкласс являются абстрактным классом AquariumFish.

Диаграмма, иллюстрирующая абстрактный класс AquariumFish и два подкласса: Shark и Plecostumus.

Шаг 2. Создайте интерфейс.

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

На следующей диаграмме представлены Sharkкласс и Plecostomusкласс, оба из которых реализуют FishActionинтерфейс.

e4b747e0303bcdaa.png

Когда следует использовать абстрактные классы, а когда — интерфейсы?

Приведенные выше примеры просты, но когда у вас много взаимосвязанных классов, абстрактные классы и интерфейсы могут помочь сделать вашу архитектуру чище, организованнее и проще в обслуживании.

Как отмечалось выше, абстрактные классы могут иметь конструкторы, а интерфейсы — нет, но в остальном они очень похожи. Итак, когда следует использовать каждый из них?

При использовании интерфейсов для проектирования класса его функциональность расширяется за счет методов, реализованных в этих интерфейсах. Использование трейтов, определенных в интерфейсах, как правило, упрощает повторное использование и понимание кода по сравнению с наследованием от абстрактного класса. Кроме того, в классе можно реализовать несколько интерфейсов, но наследовать можно только от одного класса. Общее правило – по возможности отдавать предпочтение композиции (т.е. интерфейсам и ссылкам на экземпляры) перед наследованием.

  • Используйте абстрактный класс всякий раз, когда вы не можете завершить создание класса. Например, вернувшись к AquariumFishклассу, вы можете сделать так, чтобы все классы реализовывали AquariumFishинтерфейс `<div>` FishAction, и предоставить реализацию по умолчанию для `<div>`, eatоставаясь при этом colorабстрактным, поскольку для рыб на самом деле нет цвета по умолчанию.
interface FishAction  {
    fun eat()
}

abstract class AquariumFish : FishAction {
   abstract val color: String
   override fun eat() = println("yum")
}

Last modified: Sunday, 5 April 2026, 2:10 PM