Skip to content

Latest commit

 

History

History
131 lines (108 loc) · 10.9 KB

data-classes.md

File metadata and controls

131 lines (108 loc) · 10.9 KB
type layout category title url
doc
reference
Classes and Objects
Классы данных

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

Нередко мы создаём классы, единственным назначением которых является хранение данных. Функционал таких классов зависит от самих данных, которые в них хранятся. В Kotlin класс может быть отмечен словом data:

data class User(val name: String, val age: Int)

Такой класс называется классом данных. Компилятор автоматически формирует следующие члены данного класса из свойств, объявленных в основном конструкторе:

  • пару функций equals()/hashCode(),
  • функцию toString() в форме "User(name=John, age=42)",
  • компонентные функции componentN(), которые соответствуют свойствам, в соответствии с порядком их объявления,
  • функцию copy() (см. ниже)

Если какая-либо из этих функций явно определена в теле класса (или унаследована от родительского класса), то генерироваться она не будет.

Для обеспечения согласованности и осмысленного поведения сгенерированного кода классы данных должны удовлетворять следующим требованиям:

  • Основной конструктор должен иметь как минимум один параметр;
  • Все параметры основного конструктора должны быть отмечены, как val или var;
  • Классы данных не могут быть абстрактными, open, sealed или inner;
  • (до версии 1.1) Классы данных не могут наследоваться от других классов (но могут реализовывать интерфейсы).

Дополнительно, генерация членов классов данных при наследовании подчиняется следующим правилам:

  • Если существуют явные реализации equals(), hashCode() или toString() в теле класса данных или конечные (final) реализации в суперклассе, то эти функции не генерируются, а используются существующие реализации;
  • Если суперкласс включает функции componentN(), которые являются открытыми и возвращают совместимые типы, соответствующие компонентные функции создаются для класса данных и переопределяют функции суперкласса. Если функции суперкласса не могут быть переопределены из-за несовместимости сигнатур или являются конечными (final), выдаётся сообщение об ошибке;
  • Наследование класса данных от типа, который уже имеет функцию copy(...) с совпадающей сигнатурой не рекомендуется в Kotlin 1.2 и запрещена в Kotlin 1.3;
  • Предоставление явных реализаций для функций componentN() и copy() не допускается.

Начиная с версии 1.1, классы данных могут расширять другие классы (см. примеры в статье Изолированные классы)

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

 data class User(val name: String = "", val age: Int = 0)

Свойства, объявленные в теле класса

Обратите внимание, что компилятор использует только свойства, определенные в основном конструкторе для автоматически созданных функций. Чтобы исключить свойство из автоматически созданной реализации, объявите его в теле класса:

data class Person(val name: String) {
    var age: Int = 0
}

Только свойство name будет учитываться в реализациях функций toString(), equals(), hashCode() и copy(), и будет создана только одна компонентная функция component1(). Даже если два объекта класса Person будут иметь разные значения свойств age, они будут считаться равными.

    val person1 = Person("John")
    val person2 = Person("John")
    person1.age = 10
    person2.age = 20
    println("${person1 == person2}") // выведет "true"

Копирование

Довольно часто нам приходится копировать объект с изменением только некоторых его свойств. Для этой задачи генерируется функция copy(). Для написанного выше класса User такая реализация будет выглядеть следующим образом:

fun copy(name: String = this.name, age: Int = this.age) = User(name, age)

Это позволяет нам писать:

val jack = User(name = "Jack", age = 1)
val olderJack = jack.copy(age = 2)

Классы данных и мульти-декларации

Сгенерированные для классов данных компонентные функции позволяют использовать их в мульти-декларациях:

val jane = User("Jane", 35)
val (name, age) = jane
println("$name, $age years of age") // выводит "Jane, 35 years of age"

Стандартные классы данных

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

См. также