Kotlin data class second constructor

Ключевое слово data

Если у класса указать ключевое слово data, то автоматически будут созданы и переопределены методы toString(), equals(), hashCode(), copy(). Скорее всего вы будете использовать этот вариант для создания полноценного класса-модели с геттерами и сеттерами.

 data class Client(val name: String, var postalCode: Int) 

В конструкторе класса у параметров следует указывать val или var.

Подобные классы часто используются при работе с JSON.

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

toString()

Если нужно получить информацию о классе, то достаточно вызвать имя переменной класса. Вы получите строку со всеми значениями всех свойств на основе конструктора вместо непонятных символов @Cat5edea как в Java. Такой подход удобен при тестировании и отладке. Сразу понятно, о чём идёт речь.

 data class Cat(var name: String, var age: Int) val cat = Cat("Murzik", 7) println(cat) // Cat(name=Murzik, age=7) 

Можно сразу определить значение по умолчанию у поля класса. При инициализации объекта можно не указывать поле, но оно будет доступно для вычислений.

 data class Kitten( val name: String, var lives: Int = 9 ) val murzik = Kitten("Murzik") // не указываем поле по умолчанию println(murzik.lives) val barsik = Kitten("Barsik", 8) // указываем все поля println(barsik.lives) 

equals()

При определении класса данных функция equals() (и оператор ==) по-прежнему возвращает true, если ссылки указывают на один объект. Но она также возвращает true, если объекты имеют одинаковые значения свойств, определённых в конструкторе:

 data class Cat(var name: String, var age: Int) val cat1 = Cat("Murzik", 7) val cat2 = Cat("Murzik", 7) println(cat1.equals(cat2)) // true 

Если вы переопределяете функцию equals(), также необходимо переопределить функцию hashCode().

Читайте также:  Create class instance java reflection

Кстати, если вам нужно проверить, что две переменные ссылаются на один объект, то используйте оператор ===. В отличие от оператора ==, поведение оператора === не зависит от функции equals(), которое в разных классах может вести себя по разному. Оператор === всегда ведёт себя одинаково независимо от разновидности класса.

hashCode()

Если два объекта данных считаются равными (имеют одинаковые значения свойств), функция hashCode() возвращает для этих объектов одно и то же значение:

 data class Cat(var name: String, var age: Int) val cat1 = Cat("Murzik", 7) val cat2 = Cat("Murzik", 7) val cat3 = Cat("Murzik", 5) println(cat1.hashCode()) println(cat2.hashCode()) // будет совпадать с cat1 println(cat3.hashCode()) // будет новое значение 

copy()

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

 data class Cat(var name: String, var age: Int) val cat1 = Cat("Barsik", 4) val cat2 = cat1.copy(age = 7) // у копии объекта меняем только возраст println(cat1.age) println(cat2.age) 

Фактически мы создаём копию объекта, меняем значение нужного свойства и присваиваем новый объект переменной с новым именем. При этом исходный объект остаётся без изменений.

Деструктурирующее присваивание

При создании data-классов компилятор автоматически добавляет набор функций, с помощью которых можно обратиться к свойствам. Они известны как componentN-функции, где N — это номер свойства. Подсчёт номера идёт по порядку в объявлении класса.

 data class Music(val title: String, val author: String) // Обращаемся к классу val music = Music("Kalinka", "Abba") val title = music.component1() val author = music.component2() println("$title $author") 

Мы могли бы обратиться и привычным способом.

 val title = music.title val author = music.author println("$title $author") 

Деструктуризация позволяет разбить объект на несколько переменных.

 data class Cat(var name: String, var age: Int, var city: String) val cat = Cat("Murzik", 7, "Moscow") // три переменных в одном месте val (catName, catAge, place) = cat println(catName) println(catAge) println(place) 

Можно пропустить через цикл.

 val cats = listOf(Cat("Murzik", 3, "Minks"), Cat("Barsik", 2, "Moscow")) for((catName, age, city) in cats) < println("$lives in $city") > 

Можно пропустить какую-то переменную через символ подчёркивания.

Несколько конструкторов

Добавить второй конструктор к классу можно через ключевое слово constructor.

 data class Cat(val name: String) < var age = 0; constructor(name: String, age: Int) : this(name) < this.age = age; >> 

Есть другой способ через аннотацию.

 data class Cat @JvmOverloads constructor(val name: String, val age: Int? = 0)

Источник

Creating multiple constructors for Data classes in Kotlin

It’s been a month since I joined Annyce in migrating our codebase away from Java to Kotlin. Every time she sent a PR, the amount of code she added was always remarkably lower then the amount she had taken away while still accomplishing the tasks at hand.

I started out with all our data classes so that I could get the hang of working with Kotlin, that’s after I personally went through the Kotlin Koans and read Kotlin for Android Developers by Antonio Leiva. I later moved on to migrating more complex stuff before finally graduating to writing Kotlin from scratch.

I’ll attempt to highlight all the areas that stood out to me during this period. It’s obvious but worth mentioning again that your codebase wasn’t built in a day so it’ll definitely take some time to achieve a 100% Kotlin codebase.

Creating multiple constructors for Data classes in Kotlin

aka Secondary constructors with multiple parameters

Data classes in Kotlin are immutable and it’s easy enough to create a constructor for a data class with multiple fields. Note that it’s compulsory to have a primary constructor in a data class. It’s also compulsory to have the val or var keyword before the variable name, which you can get away with in normal classes and secondary constructors.

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

What if you need several constructors in the same data class? You can use Secondary constructors to achieve this. According to the documentation,

“If the class has a primary constructor, each secondary constructor needs to delegate to the primary constructor, either directly or indirectly through another secondary constructor(s). Delegation to another constructor of the same class is done using the this keyword”

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

Another option is the @JvmOverloads annotation which generates multiple constructors based on the arguments in the constructor. Again, according to the documentation, @JvmOverloads

“Instructs the Kotlin compiler to generate overloads for this function that substitute default parameter values.

If a method has N parameters and M of which have default values, M overloads are generated: the first one takes N-1 parameters (all but the last one that takes a default value), the second takes N-2 parameters, and so on.”

data class Person @JvmOverloads constructor(val name: String, 
val age: Int? = 0)

Going by the excerpt above, the only overloaded constructor generated in this case has one parameter — val name.

Note that when you include annotations in your class declaration, you have to explicitly write out the constructor keyword just before the parenthesis. This is a lot more concise than the first option and it works as intended.

Thanks to Michael Obi and Segun Famisa for reviewing.

Thought this was great? Please don’t forget to “Recommend” and “Share”.

Источник

Оцените статью