0 added
0 removed
Original
2026-01-01
Modified
2026-03-10
1
<p>Если вы Android-инженер и задумались над переходом с Java на Kotlin, эта заметка вам пригодится.</p>
1
<p>Если вы Android-инженер и задумались над переходом с Java на Kotlin, эта заметка вам пригодится.</p>
2
<p>Ещё до того, как в Google решили объявить поддержку Kotlin на официальном уровне, нередко можно было увидеть среди рекомендаций по переходу с Java на Kotlin начать процесс перехода именно с написания unit-тестов на этом языке. Действительно, unit-тесты - наименее агрессивный путь экспансии Котлин на кодовую базу, однако вы вряд ли вы испытаете удовольствие от "сладкого" и лаконичного синтаксиса Kotlin, подталкивающее к приёмам функционального программирования, ведь в большинстве случаев unit-тесты - это императивный стиль. Но вы можете попробовать параллельно делать вкрапления языка с написания POJO посредством data-классов. О них и поговорим.</p>
2
<p>Ещё до того, как в Google решили объявить поддержку Kotlin на официальном уровне, нередко можно было увидеть среди рекомендаций по переходу с Java на Kotlin начать процесс перехода именно с написания unit-тестов на этом языке. Действительно, unit-тесты - наименее агрессивный путь экспансии Котлин на кодовую базу, однако вы вряд ли вы испытаете удовольствие от "сладкого" и лаконичного синтаксиса Kotlin, подталкивающее к приёмам функционального программирования, ведь в большинстве случаев unit-тесты - это императивный стиль. Но вы можете попробовать параллельно делать вкрапления языка с написания POJO посредством data-классов. О них и поговорим.</p>
3
<p>Допустим, вам необходимо написать класс сущности для фильма Movie. Воспользовавшись дата-классами, вы обойдётесь всего одной строчкой кода:</p>
3
<p>Допустим, вам необходимо написать класс сущности для фильма Movie. Воспользовавшись дата-классами, вы обойдётесь всего одной строчкой кода:</p>
4
data class Movie(val id: Long, val title: String, val director: String, val releaseDate: Date)<p>Что вы тут получаете помимо лаконичности: - переопределённые методы equals(), hashCode() и toString() под капотом; - Immutable-класс, который неявно наследуется от Any (в отличие от Object в Джаве) с immutable-полями и неявными публичными сеттерами и геттерами для каждого. Причём создание экземпляра такого класса выглядит так (обратите внимание, что отсутствует ключевое слово new):</p>
4
data class Movie(val id: Long, val title: String, val director: String, val releaseDate: Date)<p>Что вы тут получаете помимо лаконичности: - переопределённые методы equals(), hashCode() и toString() под капотом; - Immutable-класс, который неявно наследуется от Any (в отличие от Object в Джаве) с immutable-полями и неявными публичными сеттерами и геттерами для каждого. Причём создание экземпляра такого класса выглядит так (обратите внимание, что отсутствует ключевое слово new):</p>
5
Movie(42L, "Isle of Dogs", "Wes Anderson", Date())<p>- метод copy(), позволяющий клонировать экземпляр данного класса и полезный, если вы, к примеру, захотите создать на основе существующего объекта новый неизменяемый объект, но с отличающимися значениями одного либо нескольких полей (при условии, что они не являются private). Этот подход станет первым шагом навстречу функциональному стилю:</p>
5
Movie(42L, "Isle of Dogs", "Wes Anderson", Date())<p>- метод copy(), позволяющий клонировать экземпляр данного класса и полезный, если вы, к примеру, захотите создать на основе существующего объекта новый неизменяемый объект, но с отличающимися значениями одного либо нескольких полей (при условии, что они не являются private). Этот подход станет первым шагом навстречу функциональному стилю:</p>
6
val clonedMovie = existingMovie.copy(id = 43L)<p><em>Примечание № 1</em>: во время создания экземпляра такого класса на Java посредством copy() придётся определить значения для каждого из полей.<em>Примечание № 2</em>: этот метод недоступен для экземпляров обычных классов (не data); - ну и, наконец, мы получим поддержку значений по умолчанию, которой можно будет заменить применение Builder-паттерна.</p>
6
val clonedMovie = existingMovie.copy(id = 43L)<p><em>Примечание № 1</em>: во время создания экземпляра такого класса на Java посредством copy() придётся определить значения для каждого из полей.<em>Примечание № 2</em>: этот метод недоступен для экземпляров обычных классов (не data); - ну и, наконец, мы получим поддержку значений по умолчанию, которой можно будет заменить применение Builder-паттерна.</p>
7
data class Movie(val id: Long = 0L, val title: String = "", val director: String = "", val releaseDate: Date, val description: String? = null) ... val movie = Movie(releaseDate = Date(), title = "The Darjeeling Limited")<p>Однако учтите, что data-классы пока не могут наследоваться друг от друга. Но вам могут пригодиться неявно абстрактные sealed-классы. Допустим, тогда, когда нужно определить разные состояния загрузки данных либо экрана. И в любых других ситуациях, в которых уместны ограниченные иерархии классов.</p>
7
data class Movie(val id: Long = 0L, val title: String = "", val director: String = "", val releaseDate: Date, val description: String? = null) ... val movie = Movie(releaseDate = Date(), title = "The Darjeeling Limited")<p>Однако учтите, что data-классы пока не могут наследоваться друг от друга. Но вам могут пригодиться неявно абстрактные sealed-классы. Допустим, тогда, когда нужно определить разные состояния загрузки данных либо экрана. И в любых других ситуациях, в которых уместны ограниченные иерархии классов.</p>
8
<h2>Data-классы + Parcelable</h2>
8
<h2>Data-классы + Parcelable</h2>
9
<p>Уже начиная с версии 1.1.4, отсутствует необходимость писать boilerplate-реализации методов parcelable для поддержки сериализации и десериализации ваших объектов, ведь за вас это делает аннотация @Parcelize.</p>
9
<p>Уже начиная с версии 1.1.4, отсутствует необходимость писать boilerplate-реализации методов parcelable для поддержки сериализации и десериализации ваших объектов, ведь за вас это делает аннотация @Parcelize.</p>
10
@Parcelize data class Movie(val id: Long, val title: String, val director: String, val releaseDate: Date)<p>Просто не забудьте применить плагин Android Extensions:</p>
10
@Parcelize data class Movie(val id: Long, val title: String, val director: String, val releaseDate: Date)<p>Просто не забудьте применить плагин Android Extensions:</p>
11
apply plugin: 'kotlin-android-extensions'<p>А также надо будет определить значение experimental-флага как true.</p>
11
apply plugin: 'kotlin-android-extensions'<p>А также надо будет определить значение experimental-флага как true.</p>
12
android { ... androidExtensions { experimental = true } }<h2>Data-классы и часто используемые библиотеки</h2>
12
android { ... androidExtensions { experimental = true } }<h2>Data-классы и часто используемые библиотеки</h2>
13
<p>Если вы любите использовать такую замечательную библиотеку, как Room Persistence Library, вы всё так же при подключённом kapt сможете писать data-классы для сущностей вашей БД, которые отлично работают с Room-аннотациями.</p>
13
<p>Если вы любите использовать такую замечательную библиотеку, как Room Persistence Library, вы всё так же при подключённом kapt сможете писать data-классы для сущностей вашей БД, которые отлично работают с Room-аннотациями.</p>
14
@Entity(tableName = "movies") data class Movie( @PrimaryKey @ColumnInfo(name = "id") val id: Long, @ColumnInfo(name = "title") val title: String, @ColumnInfo(name = "director") val director: String, @ColumnInfo(name = "date") val releaseDate: Long )<p>Нет проблем и с Nullable-свойствами.</p>
14
@Entity(tableName = "movies") data class Movie( @PrimaryKey @ColumnInfo(name = "id") val id: Long, @ColumnInfo(name = "title") val title: String, @ColumnInfo(name = "director") val director: String, @ColumnInfo(name = "date") val releaseDate: Long )<p>Нет проблем и с Nullable-свойствами.</p>
15
<p>Если же говорить о библиотеках для сериализации данных, в случае с одной из наиболее популярных в этой сфере GSON, необходимость в дополнительных конвертерах и плагинах отсутствует. Библиотека станет сериализовать ваши POJO с тем же успехом, как и в случае с Java-версией:</p>
15
<p>Если же говорить о библиотеках для сериализации данных, в случае с одной из наиболее популярных в этой сфере GSON, необходимость в дополнительных конвертерах и плагинах отсутствует. Библиотека станет сериализовать ваши POJO с тем же успехом, как и в случае с Java-версией:</p>
16
data class Movie( @SerializedName("id") val id: Long, @SerializedName("title") val title: String, @SerializedName("director") val director: String, @SerializedName("releaseDate") val releaseDate: Date )<p>Но, как и в случае с Room, не поддерживаются значения по умолчанию.</p>
16
data class Movie( @SerializedName("id") val id: Long, @SerializedName("title") val title: String, @SerializedName("director") val director: String, @SerializedName("releaseDate") val releaseDate: Date )<p>Но, как и в случае с Room, не поддерживаются значения по умолчанию.</p>
17
<p>Также для совместимости Jackson c Kotlin надо добавить<a>зависимость специального модуля</a>.</p>
17
<p>Также для совместимости Jackson c Kotlin надо добавить<a>зависимость специального модуля</a>.</p>
18
<p>Схожим образом обстоят дела и с Moshi, для совместимости с которым нужно добавить зависимость:</p>
18
<p>Схожим образом обстоят дела и с Moshi, для совместимости с которым нужно добавить зависимость:</p>
19
implementation 'com.squareup.moshi:moshi-kotlin:1.x.y'<p><em><a>Источник</a></em></p>
19
implementation 'com.squareup.moshi:moshi-kotlin:1.x.y'<p><em><a>Источник</a></em></p>
20
20