Menu
Coddy logo textTech

Классы данных

Часть раздела Объектно-ориентированное программирование путешествия по Kotlin на Coddy. Урок 32 из 57.

Класс данных — это класс, основная задача которого заключается в хранении данных. Модификатор data заставляет компилятор сгенерировать toString(), equals(), hashCode(), copy() и componentN() на основе свойств первичного конструктора:

data class Point(val x: Int, val y: Int)

Внутри main:

val a = Point(1, 2)
val b = Point(1, 2)
println(a)
println(a == b)
println(setOf(a, b).size)

Вывод:

Point(x=1, y=2)
true
1

Без data println показывает имя класса и хеш-код, например Point@1b6d3586, а два объекта с одинаковыми значениями не равны. copy() создаёт новый объект с изменёнными некоторыми свойствами и оставляет исходный без изменений, поэтому класс данных со свойствами val работает как неизменяемое значение:

data class User(val name: String, val email: String, val admin: Boolean = false)

Внутри main:

val ada = User("Ada", "ada@example.com")
val boss = ada.copy(admin = true)
println(ada)
println(boss)
println(ada == boss)

Вывод:

User(name=Ada, email=ada@example.com, admin=false)
User(name=Ada, email=ada@example.com, admin=true)
false

Деструктурирующие объявления распаковывают объект в переменные: val (x, y) = point вызывают сгенерированные component1() и component2() в порядке конструктора. Это также работает в циклах for и параметрах лямбда-выражений, а _ пропускает свойство:

data class Score(val player: String, val points: Int, val level: Int)

Внутри main:

val scores = listOf(Score("ada", 120, 3), Score("bo", 95, 2))
for ((player, points) in scores) println("$player: $points")
val (_, bestPoints, bestLevel) = scores.maxBy { it.points }
println("best: $bestPoints at level $bestLevel")

Вывод:

ada: 120
bo: 95
best: 120 at level 3

Участвуют только свойства первичного конструктора: свойство, объявленное в теле класса, не включается в toString, equals, hashCode и copy. Класс данных должен иметь как минимум один параметр конструктора, и каждый параметр должен быть val или var:

data class Reading(val sensor: String, val value: Int) {
    var note = ""
}

Внутри main:

val r1 = Reading("t1", 20)
val r2 = Reading("t1", 20)
r1.note = "checked"
println(r1)
println(r1 == r2)

Вывод:

Reading(sensor=t1, value=20)
true
challenge icon

Задание

Легко

Сделайте Book классом данных, чтобы он выводился и сравнивался по значению. Затем реализуйте три функции: parse(line) преобразует Dune;Herbert;1965 в Book, newEdition(book, year) возвращает копию с новым годом и добавленным к названию (new edition), а describe(book) возвращает Dune by Herbert, 1965 с использованием деструктурирующего объявления.

Предоставленный код разбирает каждую входную строку, выводит каждую книгу, количество дубликатов, новое издание первой книги (год 2024) и описание каждой уникальной книги.

Ваш код должен находиться в Book.kt и BookTools.kt. В Main.kt содержится предоставленный код ввода/вывода, и его нельзя изменять.

Попробуйте сами

fun main() {
    // Предоставленный код ввода/вывода: оставьте его как есть
    val input = generateSequence(::readLine).toList()
    val books = input.map { parse(it) }
    for (b in books) println(b)
    println("duplicates: ${books.size - books.distinct().size}")
    println(newEdition(books[0], 2024))
    for (b in books.distinct()) println(describe(b))
}
quiz iconПроверьте себя

В этом уроке есть небольшой тест. Начните урок, чтобы ответить на вопросы и сохранить прогресс.

Все уроки раздела Объектно-ориентированное программирование

Потренируйтесь самостоятельно: Песочница Kotlin