null

Именованное деструктурирование в Kotlin

Доброго дня! Сегодня поговорим об одной из экспериментальных фич Kotlin, а именно о деструктурировании.

Проблема

Деструктурирование в Kotlin - удобная вещь. Всё работает через сгенерированные методы componentN(), и синтаксис получается приятным:

data class User(val name: String, val age: Int)
 
val user = User("Max", 23)
val (name, age) = user
println("$name is $age years old")

Но за этим удобством скрывается ловушка. Деструктурирование позиционное - значит, name получает значение component1(), age - component2(). И если кто-то решит переставить поля местами или добавить новое поле в начало класса:

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

Компилятор не выдаст ошибку, код соберётся - просто name теперь будет содержать id, а age - name. Ещё хуже ситуация с интерфейсами: деструктурировать объект через интерфейс нельзя, потому что componentN() объявляется только в data class.

В Kotlin 2.3.20 появилась экспериментальная поддержка именованного деструктурирования. Идея простая: вместо позиций используем имена свойств, а синтаксис разделяется на два вида.

Именованное деструктурирование - круглые скобки:

val (val name, val age) = user

Теперь name привязывается к свойству name, а не к позиции. Переставляйте поля
как угодно - код не сломается.
Если нужно переименовать переменную на месте:

val (val years = age, val theName = name) = user
println("$theName is $years years old")

Позиционное деструктурирование - квадратные скобки:

val [x, y] = point

Таким образом разработчики чётко разделили намерение: если хочешь по именам, то используй (), но если хочешь по позициям - то будь добр, используй [].

Деструктурирование через интерфейс

Это пожалуй самое интересное. Теперь можно деструктурировать через интерфейс, если свойства в нём объявлены:

interface Named {
    val name: String
    val age: Int
}
 
data class User(override val name: String, override val age: Int) : Named
 
fun printInfo(entity: Named) {
    val (val name, val age) = entity
    println("$name is $age")
}

Раньше такой код просто не компилировался. Теперь компилятор ищет свойство по имени и находит его в интерфейсе.

Поддержка экспериментальная, поэтому включается флагом компилятора:

-Xname-based-destructuring=only-syntax

Для Gradle:

kotlin {
    compilerOptions {
        freeCompilerArgs.add("-Xname-based-destructuring=only-syntax")
    }
}

Итог

Именованное деструктурирование - небольшое, но важное изменение. Оно убирает целый класс тихих ошибок, которые легко допустить при рефакторинге, и открывает деструктурирование для интерфейсов. Синтаксис с () и [] хорошо передаёт намерение.

Если вы активно используете деструктурирование в data-классах - стоит уже сейчас попробовать новый синтаксис и пробежаться по коду.

Next

Коротко о себе:

Работаю кем-то в компании Tune-it. На работе занимаюсь какими-то проектами, связанными с чем-то.

Nothing has been found. n is 0