Data-классы и sealed-классы в Kotlin
Классы данных
Kotlin позволяет создать класс, который содержит только данные, с помощью ключевого слова:data:
data class User(val name: String, val age: Int)
Компилятор автоматически извлекает следующие функции из первичного конструктора на основе всех объявленных свойств:
-
equals()/hashCode() -
toString()Формат выглядит так:"User(name=John, age=42)" -
componentN() functionsСоответствует свойствам, расположенным в порядке объявления. -
copy()Функции
Если эти функции уже явно определены в классе или унаследованы от суперкласса, они не будут сгенерированы.
Чтобы обеспечить согласованность и осмысленность сгенерированного кода, классы данных должны удовлетворять следующим условиям:
Первичный конструктор должен содержать как минимум один параметр.
Все параметры первичного конструктора должны быть помечены как
valилиvar;Классы данных нельзя объявлять как
abstract,open,sealedилиinner;Классы данных не могут наследоваться от других классов (но могут реализовывать интерфейсы).
Copy
Копирование использует функцию copy(). Мы можем использовать эту функцию, чтобы скопировать объект и изменить некоторые свойства. Для класса User выше его реализация будет выглядеть примерно так:
fun copy(name: String = this.name, age: Int = this.age) = User(name, age)
Пример
Используйте класс copy, чтобы скопировать класс данных User и изменить свойство age:
data class User(val name: String, val age: Int)
fun main(args: Array<String>) {
val jack = User(name = "Jack", age = 1)
val olderJack = jack.copy(age = 2)
println(jack)
println(olderJack)
}
Результат вывода:
User(name=Jack, age=1) User(name=Jack, age=2)
Классы данных и деструктурирующие объявления
Компонентные функции позволяют использовать классы данных в деструктурирующих объявлениях:
val jane = User("Jane", 35)
val (name, age) = jane
println("$name, $age years of age") // prints "Jane, 35 years of age"
Стандартные классы данных
Стандартная библиотека предоставляетPairиTripleВ большинстве случаев именованные классы данных — лучший выбор при проектировании, потому что они делают код более читаемым и предоставляют осмысленные имена и свойства.
Герметичные классы
Герметичные классы используются для представления ограниченных иерархий наследования классов: когда значение относится к ограниченному набору типов и не может относиться к какому-либо другому типу. В некотором смысле они являются расширениями перечисляемых классов: набор значений перечисляемого типа также ограничен, но каждая константа перечисления существует только в единственном экземпляре, тогда как подкласс герметичного класса может иметь несколько экземпляров, которые могут содержать состояние.
Чтобы объявить герметичный класс, используйтеsealedдля модификации класса. Герметичные классы могут иметь подклассы, но все подклассы должны быть вложены внутрь герметичного класса.
sealed не может изменять интерфейс или абстрактный класс (будет выдано предупреждение, но ошибки компиляции не возникнет).
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
}
Ключевое преимущество использования sealed-классов заключается в том, что при использовании выражения 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` 子句,因为我们已经覆盖了所有的情况
}
Другие расширения