Data Class

https://kotlinlang.org/docs/data-classes.html
data class

Специальный тип класса, который упрощает создание классов для хранения данных. Они автоматически предоставляют множество полезных функций, что делает их идеальными для использования в качестве контейнеров для данных.

• Не может быть суперклассом, от data-класса нельзя унаследоваться.

Должен содержать хотя бы одно поле val или var в главном конструкторе.

• При генерации методов учитываются только поля из первичного конструктора.

• Не поддерживает модификаторы open abstract sealed inner.

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

• Если в суперклассе реализованы equals hashCode toString - data-класс будет использовать их.

• Поддерживает декомпозицию на переменные с помощью функций componentN.

Methods
equals

Сравнивает два объекта data-класса по значениям их полей, а не по ссылкам. Это удобно для проверки эквивалентности объектов.

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

val person1 = Person("John", 30)
val person2 = Person("John", 30)
println(person1 == person2)  // true, так как поля одинаковые
hashCode

Компилятор автоматически генерирует hashCode на основе значений всех свойств. Это важно для использования объектов в коллекциях, таких как HashMap или HashSet.

val personSet = hashSetOf(Person("John", 30))
println(personSet.contains(Person("John", 30)))  // true
toString

Автоматически создаётся строковое представление объекта, которое выводит названия полей и их значения. Это упрощает отладку.

val person = Person("John", 30)
println(person)  // Вывод: Person(name=John, age=30)
copy

Позволяет создавать копии объекта с возможностью изменения некоторых его полей.

val person1 = Person("John", 30)
val person2 = person1.copy(age = 31)
println(person2)  // Вывод: Person(name=John, age=31)
componentN

Для каждого свойства генерируются методы доступа component1(), component2(), и т.д., которые позволяют использовать деструктуризацию.

val person = Person("John", 30)
val (name, age) = person  // Деструктуризация
println(name)  // John
println(age)   // 30
Destructuring Declarations
https://kotlinlang.org/docs/destructuring-declarations.html

Сопоставление с образцом позволяет легко извлекать и присваивать значения из объектов или коллекций нескольким переменным одновременно. Это удобный способ разбора сложных структур данных на отдельные компоненты.

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

fun main() {
    val person = Person("Alice", 30)
    
    // Сопоставление с образцом
    val (name, age) = person
    
    println("Name: $name, Age: $age") // Выведет: Name: Alice, Age: 30
}
Вопросы на собесе (27)
  • constructor (4)
    1. Как рекомендуется объявлять переменные в конструкторе data-класса?

      Использовать val для неизменяемых полей (код более безопасный и предсказуемый).

    1. Можно ли описать data-класс с пустым конструктором, но с полями внутри класса?

      Нет нельзя.

    1. Можно ли создавать вторичные конструкторы в data class?

      Да, но они должны вызывать первичный конструктор, чтобы корректно инициализировать все свойства класса.

    1. Как будут вычесляться методы если указать вторичный конструктор?

      Если в data class указать вторичный конструктор, то методы equals, hashCode, toString, и copy будут по-прежнему вычисляться на основе свойств, объявленных в главном конструкторе, даже если вы добавите дополнительные конструкторы

  • Methods (7)
    1. Перечисли методы data-класса?

      equals() hashCode() toString() сopy() componentN()

    1. Kакие методы переопределены в data class?

      equals() hashCode() toString()

    1. На основе чего вычисляются методы в data class?

      На основе полей в первичном конструкторе.

    1. Как работает метод copy?

      Позволяет создавать новый экземпляр объекта с измененными значениями выбранных свойств. Это сохраняет неизменяемость исходного объекта и позволяет легко создавать модифицированные копии.

    1. Что произойдет если вызвать метод copy без аргументов?

      Будет создан новый объект, идентичный исходному, с копированием всех значений свойств. То есть, будет скопирован объект с теми же параметрами, что и у оригинала.

    1. Какой будет результат если сравнить через === data-класс и его копию созданную через copy() без аргументов?

      Результат будет false, так как это два разных объекта в памяти.

    1. Как работает метод componentN?

      Методы componentN автоматически генерируются для data class и возвращают свойства объекта по порядку. Это позволяет удобно извлекать значения, например, при деструктивном присваивании.

  • Другие (16)
    1. Что такое data class в Kotlin?

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

    1. Какие ограничения есть у data class?

      Минимум 1 параметр в первичном конструкторе val или var.

      Не может быть abstract open sealed inner.

    1. Как data-класс вычисляет хэшкод?

      На основе всех полей, объявленных в первичном конструкторе класса. Используется метод Objects.hash() из Java.

    1. Что сделать чтобы поле не участвовало в вычислении хэшкода в data-классе?

      Перенести поле из первичного конструктора в тело data-класса.

      Переопределить и переписать метод hashCode().

    1. Можно ли наследоваться от data class? (расширить его)

      Нет.

    1. Почему в Kotlin запретили наследоваться от data-класса?

      В Kotlin запретили наследование от data-классов, чтобы сохранить целостность и корректность автоматически сгенерированных методов, таких как equals, hashCode, toString и copy. Наследование могло бы привести к нарушению этих свойств, так как дочерний класс мог бы изменять поведение или данные, что привело бы к некорректным результатам в этих методах.

    1. Как будут использоваться поля при наследовании data class от другого класса?

      Поля с одинаковыми именами должны быть переопределены в data class с помощью override и быть open в базовом классе.

    1. Есть ли в Kotlin средства чтобы не приходилось вручную переопределять equals и hashCode для классов которые мы создаем?

      Да, data-классы.

    1. В чем может быть опасность объявления лямбды в первичном конструкторе data-класса?

      Если в data-классе лямбда указана в конструкторе, то два экземпляра, даже с одинаковыми параметрами, могут вернуть false при сравнении, так как лямбды не равны друг другу по значению. Каждый экземпляр лямбды является уникальным, поэтому equals вернёт false, даже если они функционально идентичны.

    1. Как хранить лямбды в data-классе, чтобы объекты корректно сравнивались?

      Хранить их вне конструктора, в теле класса. Это исключит их из equals и hashCode, сохраняя корректное сравнение объектов.

    1. Сколько объектов будет в set и какие именно?
      data class Dog(
      		val breed: String = "shepherd"
      ) {
      		var name = ""
      }
      
      val set = hashSetOf(
      		Dog().apply { name = "Joe" },
      		Dog("sharik"),
      		Dog().apply { name = "Henry" }
      )
      println(set)

      В Set будет 2 объекта: [Dog(breed=shepherd), Dog(breed=sharik)].

      1. Dog(breed = “shepherd”).name = “Joe”.
      1. Dog(breed = “sharik”).name = “”.
    1. Каким будет результат сравнения двух объектов?
      data class Person(
      		val name: String
      ) {
      		var age: Int = 0
      }
      
      val person1 = Person("John")
      val person2 = Person("John")
      person1.age = 10
      person2.age = 20
      
      person1 == person2 ???

      Результат будет true. Метод equals автоматически сгенерирован так, что он сравнивает только параметры конструктора (в данном случае только name). Поле age не участвует в сравнении, так как оно не является частью первичного конструктора.

    1. Что напечатает println?
      data class A(
          var field: String = ""
      )
      
      fun func(a: A) {
          a.field = "d"
      }
      
      val a = A()
      a.field = "c"
      func(a)
      print(a.field)

      d

    1. Какой из указанных модификаторов нельзя использовать с data-классом?

      inner

      open

      abstract

      Ни один из перечисленных.

    1. Как нельзя объявлять data class?

      data class MyClass(val a: Int)

      open data class MyClass(val a: Int)

      data class MyClass(val a: Int): Base()

      data class MyClass()

    1. Что могут делать data классы из нижеперечисленного? (Может быть несколько вариантов ответов)

      Наследоваться от других дата классов.

      Наследоваться от других классов.

      Имплементировать интерфейсы.

      Не могут все вышеперечисленное.