Урок 22: Data class (дата классы) copy, toString, equals, hashCode в Kotlin

Урок 22: Data class (дата классы) copy, toString, equals, hashCode в Kotlin

Data классы

Я уже рассказывал вам про классы и ООП, начиная с 11 урока. И вы понимаете насколько это мощный инструмент, позволяющий писать код приближенный к реальности.

Классические классы используются для организации кода, реализации ООП подходов. Создатели языка Kotlin сделали свой тип классов, специально для хранения данных –Data классы. Строго говоря, это обычные классы у которых есть дополнительные возможности. Со временем вы полностью почувствуете разницу, пока лишь сосредоточусь на основных отличиях, которые легко понять и запомнить.

Что значит для хранения данных? Все то, что мы делали раньше, когда создавали и описывали сущности телефонных справочников, космических кораблей, словарей для переводчиков и так далее.

Дополнительные возможности Data классов

Какими дополнительными возможностями обладают Data классы в сравнении с обычными? Если совсем коротко, то для объектов этих классов можно вызвать новые методы. Такие как toString(), equals(), hashCode() и copy().

  • equals() – производит сравнение двух объектов;
  • hashCode() – позволяет получить хэш-код объекта (уникальный целочисленный номер объекта);
  • toString() – возвращает представление объекта в виде понятной строки (именно понятной, об этом будет подробнее ниже);
  • copy() – копирует данные объекта в другой объект.

Метод toString()

Понятнее всего будет показать ценность этого метода, сперва обозначив проблему. При разработке телеграм бота для изучения иностранных слов мы выделили сущность, которая должна хранить слово на одном языке и его перевод. Создадим классический класс Word с полями text и translate. Тут же создадим объект “слова” и попытаемся распечатать его в консоль.

class Word(
	val text: String,
	val translate: String,
)

fun main(){
	val word = Word("Red", "Красный")
	println(word)
}

Получаем непонятный набор символов additional.Word@452b3a41. Так выглядит строковое представление объекта по умолчанию. Но мы хотим увидеть в строке понятные данные объекта. Как решить эту проблему?

Вариант 1. Используя классический класс. Может быть к объекту добавить принудительное приведение к строке? Нет, *println*(word.toString()) не сработает. Так как метод println() под капотом использует тот же toString() и результат будет абсолютно таким же. Окей. Но можно переопределить стандартный toString(), добавив в него нужное нам поведение.

Для этого в теле класса сочетанием клавиш cmd+N сгенерируем toString(). В следующем окне IDEA предложит выбрать поля объекта, для которых будет создано новое текстовое представление. Оставляем оба.

Получаем переопределенную функцию toString(). По кружочку со стрелкой можно переместиться к исходной функции, которую переопределили. Теперь метод возвращает красивую строку с полями объекта. Запускаем и видим в консоли ожидаемый результат Word(text=’Red’, translate=’Красный’).

class Word(
	val text: String,
	val translate: String,
) {
	override fun toString(): String {
		return "Word(text='$text', translate='$translate')"
	}
}

Вариант 2. Добавить ключевое слово data к классу и просто вызвать печать объекта. Тело класса уже не нужно. Запускаем и получаем точно такой же результат.

fun main(){
	val word = Word("Red", "Красный")
	println(word)
}

data class Word(
	val text: String,
	val translate: String,
)fun main(){
	val word = Word("Red", "Красный")
	println(word)
}

data class Word(
	val text: String,
	val translate: String,
)

При необходимости здесь тоже можно переопределять toString(). Если вдруг нас не будет устраивать реализация по умолчанию.

Метод equals()

Эта функция занимается сравнением и аналогична оператору “двойное равно” (==) и по аналогии с тустринг ее можно сгенерировать с помощью среды разработки. Можете самостоятельно посмотреть ее реализацию.

Добавим несколько объектов для сравнения. Если попробовать сравнить пару через equals(), среда разработки предложит заменить функцию на оператор. Так и сделаем.

fun main(){
	val word1 = Word("Red", "Красный")
	val word2 = Word("Red", "Красный")
	val word3 = Word("White", "Белый")
	println(word1 == word2)
	println(word2 == word3)
}

При сравнении обычных классов оба вывода будет false. Потому, что сравниваются ссылки на объекты в памяти, а не значения. Но Data класс позволяет сравнить именно проинициализированные данные. Поэтому видим закономерный результат true и false.

И еще, помните про оператор ссылочного сравнения? Тройное равно (===). Он никак не относится к equals() и ведет себя одинаково в не зависимости от типа класса.

Еще одна интересная функция из арсенала Data классов – copy(). Во-первых – она умеет копировать объекты целиком.

fun main(){
	val word1 = Word("Red", "Красный")
	val word2 = Word("Red", "Красный")
	val word3 = Word("White", "Белый")
//	println(word1 == word2)
//	println(word2 == word3)

	val word4 = word3.copy()
	println(word4)
}

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

fun main(){
	val word1 = Word("Red", "Красный")
	val word2 = Word("Red", "Красный")
	val word3 = Word("White", "Белый")
//	println(word1 == word2)
//	println(word2 == word3)

	val word4 = word3.copy(translate = "Правильный перевод: Белый")
	println(word3)
	println(word4)
}

Это может применяться, например, в мобильной разработке. Когда есть экран, для его полей есть какой-то Data класс. И в определенный момент в состоянии экрана меняется только одно конкретное поле. И чтобы отрисовать это новое измененное состояние, с сохранением остальных полей, применяется функция копирования объекта. Повторюсь, метод copy() доступен только для Data классов.

Метод hashCode()

Этот метод есть в обоих разновидностях класса, но в Data классе он переопределен и имеет другую реализацию. Метод возвращает уникальный код объекта. Он также может использоваться для сравнения, как и equals(), но работает быстрее. Потому, что два числа сравнивать быстрее, чем все свойства и значения объекта. Если два объекта Data класса имеют одинаковые свойства, то и хэш-код у них будет одинаковый.

println(word1.hashCode())
println(word2.hashCode())

У одинаковых data-классов одинаковый хэшкод, потому что для них генерируется hashcode() функция основанная на полях внутри классов.

public int hashCode() {
      String var10000 = this.original;
      int var1 = (var10000 != null ? var10000.hashCode() : 0) * 31;
      String var10001 = this.translate;
      return var1 + (var10001 != null ? var10001.hashCode() : 0);
   }

Обычные же, не дата классы использую hashcode() базового класса Object, реализация которого игнорирует поля класса, и для упрощения можно сказать, что hashcode генерируется случайным образом для каждого объекта (зависит от реализации JVM).

public class Object {
	//...
	@IntrinsicCandidate
	public native int hashCode();
	//...
}

Подробнее мы не будем закапываться на этом уровне изучения языка. Если вам интересен более глубокий разбор исходников и поиск причинно-следственных связей – ссылку на материал оставлю в описании.

И это конец. Data классы, их возможности и другие продвинутые темы (которые не освещены на канале) мы используем в курсовой работе по написанию своего телеграм бота на Kotlin.

Бесплатные Telegram-боты для обучения

Практика с проверкой кода и помощью ИИ-ментора

AndroidSprint AI Mentor

Проверяет Pull Request'ы в GitHub, проводит тестовые собеседования с голосом и таймером, помогает разбираться с кодом 24/7

Попробовать ИИ-ментора →

KotlinSprint Bot

22 урока Kotlin, 220 тестов, 120 практических задач с код-ревью

Начать обучение Kotlin →

AndroidSprint Bot

Тесты по Android SDK, Jetpack Compose, архитектуре приложений

Тесты по Android →

Бесплатная диагностика с сениор разработчиком

Запишитесь на консультацию — перезвоним в рабочие часы

Или свяжитесь удобным способом

Вам также может быть интересно

Узнать подробнее
Курс AndroidSprint

Глубокое обучение Android разработке с 0 до получения оффера. Только персональная практика с гарантией получения продуктового опыта.

Курс AndroidSprint - Глубокое <strong>обучение Android разработке с 0 до получения оффера</strong>. Только персональная практика с гарантией получения продуктового опыта.
Узнать подробнее
Узнать подробнее
Практикум по Kotlin

Изучение Котлин с 0 для профессиональной разработки. Личный ментор и разбор кода задач через git-flow.

Практикум по Kotlin - Изучение Котлин <strong>с 0 для профессиональной разработки</strong>. Личный ментор и разбор кода задач через git-flow.
Узнать подробнее
Узнать подробнее
Бесплатные уроки по Kotlin разработке

Самостоятельное освоение базы по языку для дальнейшего развития в Android/back-end разработке или в автотестах.

Бесплатные уроки по Kotlin разработке - <span>Самостоятельное освоение базы по языку для дальнейшего развития в Android/back-end разработке или в автотестах.</span>
Узнать подробнее
Узнать подробнее
Onboarding в разработку

Полное обучение Android разработке с нуля до получения оффера. Делаем упор на практику и обратную связь

Onboarding в разработку - <span>Полное обучение Android разработке с нуля до получения оффера. Делаем упор на практику и обратную связь</span>
Узнать подробнее
Узнать подробнее
Обучающий Kotlin телеграм бот (с тестами)

Ваш основной инструмент для изучения основ языка. Бесплатные тесты и практика внутри.

Обучающий Kotlin телеграм бот (с тестами) - Ваш основной <span>инструмент для изучения основ языка.</span> Бесплатные тесты и практика внутри.
Узнать подробнее
Узнать подробнее
Бесплатные уроки по Android разработке

Самостоятельное обучение разработке Андроид приложений. Понятные видеоуроки с разжеванными примерами.

Бесплатные уроки  по Android разработке - Самостоятельное <span>обучение разработке Андроид приложений.</span> Понятные видеоуроки с разжеванными примерами.
Узнать подробнее
Узнать подробнее
Курс по UI/Unit тестированию

Для ручных тестировщиков, которые готовы осваивать автотесты с использованием актуального стека технологий. [в разработке]

Курс по UI/Unit тестированию - Для ручных тестировщиков, которые <span>готовы осваивать автотесты</span> с использованием актуального стека технологий. [в разработке]
Узнать подробнее
Узнать подробнее
Обучающий Android телеграм бот (с тестами)

Бесплатные теоретические тесты для самопроверки. А также информер на практических спринтах по Android.

Обучающий Android телеграм бот (с тестами) - Бесплатные <span>теоретические тесты для самопроверки.</span> А также информер на практических спринтах по Android.
Узнать подробнее
ИИ-ментор код-ревью и голосовые собесы
Открыть