type | layout | category | title | url |
---|---|---|---|---|
doc |
reference |
Classes and Objects |
Изолированные классы |
Изолированные классы используются для отражения ограниченных иерархий классов, когда значение может иметь тип только из ограниченного набора, и никакой другой. Они являются, по сути, расширением enum-классов: набор значений enum типа также ограничен, но каждая enum-константа существует только в единственном экземпляре, в то время как наследник изолированного класса может иметь множество экземпляров, которые могут нести в себе какое-то состояние.
Чтобы описать изолированный класс, укажите модификатор sealed
перед именем класса. Изолированный класс может иметь наследников, но все они должны быть объявлены в том же файле, что и сам изолированный класс. (До версии Kotlin 1.1 правила были ещё более строгими: классы должны были быть вложены в объявлении изолированного класса).
sealed class Expr
data class Const(val number: Double) : Expr()
data class Sum(val e1: Expr, val e2: Expr) : Expr()
object NotANumber : Expr()
fun eval(expr: Expr): Double = when (expr) {
is Const -> expr.number
is Sum -> eval(expr.e1) + eval(expr.e2)
NotANumber -> Double.NaN
}
(Пример выше использует одну новую возможность Kotlin 1.1: расширение классов, включая изолированные, классами данных)
Сам по себе изолированный класс является абстрактным, он не может быть создан напрямую и может иметь абстрактные компоненты.
Также конструктор изолированного класса не может быть не приватным (их конструкторы приватны по умолчанию).
Обратите внимание, что классы, которые расширяют наследников изолированного класса (непрямые наследники) могут быть помещены где угодно, не обязательно в том же файле.
Ключевое преимущество от использования изолированных классов проявляется тогда, когда вы используете их в выражении when. Если возможно проверить что выражение покрывает все случаи, то вам не нужно добавлять else
.
fun eval(expr: Expr): Double = when(expr) {
is Expr.Const -> expr.number
is Expr.Sum -> eval(expr.e1) + eval(expr.e2)
Expr.NotANumber -> Double.NaN
// оператор `else` не требуется, потому что мы покрыли все возможные случаи
}