Закрытый класс и Закрытый интерфейс — две функции Kotlin, которые позволяют создавать ограниченные иерархии классов. Обе эти конструкции используются для определения конечного набора возможных подтипов и предотвращения определения дополнительных подтипов за пределами объявленной иерархии.

Запечатанный класс:

Запечатанный класс — это класс, который может быть подклассом, но только внутри того же файла, в котором он объявлен. Это означает, что запечатанный класс не может быть создан напрямую и не может иметь никаких других подклассов вне файла, в котором он объявлен. Он обычно используется для представления ограниченной иерархии классов.

Вот пример закрытого класса:

sealed class Result<out T> {
    data class Success<T>(val data: T) : Result<T>()
    data class Error(val error: Exception) : Result<Nothing>()
}

В этом примере у нас есть закрытый класс с именем Result, который имеет два подкласса, Success и Error. Подкласс Success принимает универсальный тип T в качестве параметра, который представляет данные, возвращаемые в случае успеха. Подкласс Error принимает Exception в качестве параметра, который представляет возникшую ошибку.

  • Закрытые классы могут иметь конструкторы с параметрами, а запечатанные интерфейсы — нет.
  • Закрытые классы могут иметь абстрактные методы и свойства, тогда как запечатанные интерфейсы могут иметь только абстрактные методы.
  • Закрытые классы могут быть расширены классами, объектами и другими запечатанными классами, тогда как запечатанные интерфейсы могут быть реализованы только классами и объектами.
  • Закрытые классы часто используются в сочетании с выражениями when, чтобы обеспечить исчерпывающее сопоставление с образцом.

Пример:

sealed class Animal
class Dog(val name: String): Animal()
class Cat(val name: String): Animal()

fun makeSound(animal: Animal) = when (animal) {
    is Dog -> println("${animal.name} says woof!")
    is Cat -> println("${animal.name} says meow!")
}

val myDog = Dog("Rufus")
val myCat = Cat("Whiskers")
makeSound(myDog) // outputs: "Rufus says woof!"
makeSound(myCat) // outputs: "Whiskers says meow!"

Герметичный интерфейс:

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

Вот пример закрытого интерфейса:

sealed interface State {
    object Idle : State
    data class Loading(val message: String) : State
    data class Error(val error: Throwable) : State
    data class Success(val data: Any) : State
}

В этом примере у нас есть запечатанный интерфейс с именем State, который имеет четыре подтипа: Idle, Loading, Error и Success. Каждый подтип представляет различные состояния, в которых может находиться приложение, например, когда приложение бездействует, загружает данные, сталкивается с ошибкой или успешно извлекает данные.

  • Запечатанные интерфейсы не могут иметь конструкторов с параметрами, но могут иметь свойства.
  • Закрытые интерфейсы могут иметь только абстрактные методы, но они также могут иметь реализации по умолчанию для этих методов.
  • Закрытые интерфейсы могут быть реализованы классами и объектами, но не могут быть расширены другими интерфейсами или запечатанными интерфейсами.
  • Закрытые интерфейсы часто используются как способ определения набора связанных функций, которые могут быть реализованы различными классами.

Пример:

sealed interface Animal {
    val name: String
    fun makeSound()
}

class Dog(override val name: String): Animal {
    override fun makeSound() {
        println("$name says woof!")
    }
}

class Cat(override val name: String): Animal {
    override fun makeSound() {
        println("$name says meow!")
    }
}

val myDog = Dog("Rufus")
val myCat = Cat("Whiskers")
myDog.makeSound() // outputs: "Rufus says woof!"
myCat.makeSound() // outputs: "Whiskers says meow!"

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

Когда использовать запечатанные классы против запечатанных интерфейсов:

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

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

Таким образом, запечатанные классы и запечатанные интерфейсы — это мощные функции Kotlin, которые позволяют разработчикам определять ограниченные иерархии классов и предотвращают определение дополнительных подтипов за пределами объявленной иерархии. Они полезны в ситуациях, когда необходим конечный набор возможных подтипов, и могут помочь сделать код более надежным и менее подверженным ошибкам.