Запечатанные классы в Котлине

Запечатанные Классы в Kotlin

1. Вступление

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

2. Что такое запечатанный класс?

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

Они полезны, когда у вас очень строгая иерархия наследования с определенным набором возможных подклассов и без других. The compiler guarantees that only classes defined in the same source file as the sealed class are able to inherit from it.с

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

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

3. Когда использовать запечатанные классы?

Sealed classes are designed to be used when there are a very specific set of possible options для значения, и где каждая из этих опций функционально отличается - простоAlgebraic Data Types.

Общие варианты использования могут включать реализациюState Machine илиMonadic Programming, что становится все более популярным с появлением концепций функционального программирования.

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

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

4. Написание запечатанных классов

Начнем с написания нашего собственного запечатанного класса - хорошим примером такой запечатанной иерархии являетсяOptional из Java 8 - который может быть либоSome, либоNone..

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

Таким образом, мы можем реализовать это:

sealed class Optional {
    // ...
    abstract fun isPresent(): Boolean
}

data class Some(val value: V) : Optional() {
    // ...
    override fun isPresent(): Boolean = true
}

class None : Optional() {
    // ...
    override fun isPresent(): Boolean = false
}

Теперь можно гарантировать, что каждый раз, когда у вас есть экземплярOptional<V>, на самом деле у вас есть либоSome<V>, либо None<V>.

В Java 8 реальная реализация выглядит иначе из-за отсутствия закрытых классов.

Затем мы можем использовать это в наших вычислениях:

val result: Optional = divide(1, 0)
println(result.isPresent())
if (result is Some) {
    println(result.value)
}

Первая строка вернет либоSome, либоNone. Затем мы выводим, получили ли мы результат.

5. Использовать с Когда

Kotlin поддерживает использование запечатанных классов в своих конструкцияхwhen. Поскольку всегда существует точный набор возможных подклассов,the compiler is able to warn you if any branch is not handled, in exactly the same way that it does for enumerations.

Это означает, что в таких ситуациях обычно нет необходимости в комплексном обработчике, что, в свою очередь, означает, что добавление нового подкласса автоматически безопасно - компилятор немедленно предупредит вас, если вы не обработали его, и вам понадобится чтобы исправить такие ошибки, прежде чем продолжить.

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

val message = when (result) {
    is Some -> "Answer: ${result.value}"
    is None -> "No result"
}
println(message)

Если одна из двух ветвей отсутствовала, это не скомпилировало бы и вместо этого привело бы к ошибке:

'when' expression must be exhaustive, add necessary 'else' branch

6. Резюме

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

Как всегда, фрагменты кода можно найтиover on GitHub.