Search⌘ K
AI Features

Classes and Objects (part-II)

Explore Kotlin's data classes that automatically generate equals, hashCode, and toString methods to simplify data handling. Understand how init blocks allow complex code during object initialization and how secondary constructors provide multiple ways to create instances.

The data classes

We can take this one step further by writing the keyword, data, before the compact class:

Kotlin 1.5
data class Person(val firstName: String, val lastName: String)

With this minimal addition, we have created a data class. This is extremely useful for classes whose primary task is that of a data container.

Data classes have a lot of useful features for this purpose. One of these, however, is a real relief for Java developers. The Kotlin compiler automatically generates proper equals() and hashCode() methods.

To do this, it considers all the properties declared in the primary constructor, so that, for example, two instances of our Person data class are structurally equal if their firstName and lastName match.

As a further addition, a toString() method is also created. This converts the object to a string of the form Person(firstName = Alex, lastName = Prufrock).

Kotlin 1.5
data class Person(val firstName: String, val lastName: String)
fun main()
{
val p1 = Person("Alex", "Prufrock")
println(p1.toString())
val p2 = Person("Alex", "Prufrock")
println(p2.toString())
println(p1.equals(p2))
}

The init blocks

Primary constructors are a great thing and make for the utmost concise code. Unfortunately, they have the disadvantage that they cannot contain any further instructions except the implicit assignment of their arguments to properties.

To execute more complex code during initialization, we use secondary constructors or init blocks:

Kotlin 1.5
class Person(val firstName: String, val lastName: String) {
init {
println("Person object created")
}
}
fun main()
{
Person("Alex", "Prufrock")
}

These are executed during instantiation in the order in which they are declared in the class’s body. There may be more than one init block.

Secondary constructors

Like Java, Kotlin allows for more than one constructor for a class. These are called secondary constructors:

Kotlin 1.5
class Person(val firstName: String, val lastName: String) {
constructor(firstName: String, lastName: String, parent: Person) : this(firstName, lastName) {
parent.children += this
}
}

They are declared using the constructor keyword. If there is a primary constructor, it must always be called. To do this, we reference it with this and call it separated by a :.

Kotlin 1.5
class Person(val firstName: String, val lastName: String) {
// creates a modifiable list which is initially empty.
val children : MutableList<Person> = mutableListOf() // empty list
constructor(firstName: String, lastName: String, parent: Person) : this(firstName, lastName) {
parent.children += this
}
}
fun main()
{
val p1 = Person("Alex", "Prufrock")
Person ("Jane", "Prufrock", p1)
println (p1.firstName + " is " + p1.children[0].firstName + "'s parent")
}

In addition to the features described, Kotlin classes have much more to offer, and the already presented syntax has even more in store. To do justice to the title of this course, though, we leave it at this brief introduction, which suffices for understanding the upcoming lessons.