data class — зачем нужен
data class — класс, который нужен для хранения данных: точка с координатами, пользователь, товар. Достаточно перечислить свойства, а полезные методы компилятор добавит сам:
class PlainPoint(val x: Int, val y: Int)
data class Point(val x: Int, val y: Int)
fun main() {
println(Point(1, 2))
println(Point(1, 2) == Point(1, 2))
println(PlainPoint(1, 2) == PlainPoint(1, 2))
val p = Point(3, 4)
val moved = p.copy(x = 10)
println(moved)
val (x, y) = moved
println(x + y)
val set = setOf(Point(1, 2), Point(1, 2), Point(5, 5))
println(set.size)
}Point(x=1, y=2)
true
false
Point(x=10, y=4)
14
2Что добавляется:
| Метод | Что даёт |
|---|---|
toString() | вывод вида Point(x=1, y=2) вместо PlainPoint@6d06d69c |
equals() | == сравнивает свойства: одинаковые данные — равные объекты |
hashCode() | одинаковые объекты считаются одним в Set и одним ключом в Map |
copy() | копия с изменением нужных свойств: p.copy(x = 10) |
component1(), component2()… | деструктуризация: val (x, y) = p |
У обычного класса два объекта с одинаковыми свойствами не равны: == у него проверяет, один ли это объект. Поэтому PlainPoint(1, 2) == PlainPoint(1, 2) — false, а в множество попали бы обе точки.
Правила объявления
- в основном конструкторе — хотя бы один параметр;
- все параметры основного конструктора — с
valилиvar; - data class не может быть
abstract,open,sealedилиinner, то есть от него нельзя унаследоваться.
Методы и другие свойства в теле класса разрешены:
data class Product(val name: String, val price: Int) {
fun withDiscount(percent: Int) = copy(price = price * (100 - percent) / 100)
}
fun main() {
val p = Product("Tea", 300)
println(p.withDiscount(10))
}Product(name=Tea, price=270)Где используют
- данные, пришедшие из сети или из файла в виде JSON, — для каждого вида записи свой data class;
- состояние экрана в приложениях Android:
data class UiState(val loading: Boolean, val items: List<String>); - возврат нескольких значений из функции:
data class MinMax(val min: Int, val max: Int).
Подводные камни
- В
toString,equalsиcopyучаствуют только свойства из конструктора. Свойство, объявленное в теле класса, при сравнении не учитывается: два объекта с разными значениями такого свойства будут равны. varв data class внутриSetили ключомMap. Если поменять свойство уже после добавления, объект «потеряется»:containsперестанет его находить. Для data class лучшеval, а изменения — черезcopy.copyкопирует неглубоко. Если свойство — изменяемый список, у копии и оригинала он один и тот же.
Похожее в Java — record (с Java 16), но он только для неизменяемых данных и без copy. Подробнее о классах — в уроке Классы, data class и object.