Kotlin Data Classes 101: Understanding Syntax, Usage and Inheritance
Efficient Data Structuring in Android Development with Kotlin's Data Classes
Welcome back to this series of articles on Android Development with Kotlin and Jetpack Compose. In the previous article, we discussed the type system in Kotlin along with null safety. In this article, we will take a look at what data classes are, how they differ from regular classes, and how to use them effectively in your Android app development.
Kotlin provides us with a special modifier for classes - data
. Such classes are known as data classes. They are typically used to represent a data model in an application, such as a user or a product. These data classes provide useful functionalities out f the box, such as equals()
, hashCode()
, and toString()
methods, which are automatically generated for us by the compiler. This can save us a lot of time and make our code more readable and maintainable.
Let's get started!
Syntax And Usage
Let's see how can we model a User
class using data classes. We'll store two properties name
and age
.
To declare a data class use the data
keyword, followed by the class declaration. Make sure that there is at least one property in the primary constructor.
data class User(val name: String, val age: Int)
The val
keyword in front of the properties indicates that they are read-only, and can only be initialized in the primary constructor. We can also use var
to make the properties mutable.
We can create an instance of a data class similar to that of regular classes. For example:
val user = User("John", 30)
We can then access the properties of the User
class using the dot notation:
println(user.name) // prints "John"
println(user.age) // prints 30
One of the main benefits of data classes is that they come with several automatically generated functions that make it easy to work with instances of the class. These functions include:
equals()
: This function compares two instances of the data class for equality. It is generated based on the properties defined in the primary constructor.hashCode()
: This function generates a unique hash code for an instance of the data class. It is also generated based on the properties defined in the primary constructor.toString()
: This function generates a string representation of an instance of the data class. The default implementation includes the class name and the values of the properties.
Let's see how we can use the equals()
function to compare two instances of the User
class:
val user1 = User("John", 30)
val user2 = User("John", 30)
val user3 = User("Jane", 25)
println(user1 == user2) // prints true
println(user1 == user3) // prints false
We can also use the toString()
function to get a string representation of an instance of the data class:
val user = User("John", 30)
println(user) // prints "User(name=John, age=30)"
In this section, we covered the syntax and usage of data classes in Kotlin, including how to define a data class, create an instance of a data class, access its properties, and use the automatically generated functions such as equals()
, hashCode()
, and toString()
.
Properties Declared in the class body
In the previous section, we took a look at how to create a data class. We declared the properties in the primary constructor but we can also declare some properties in the class body. These properties are not considered part of the primary constructor and are not included in the automatically generated functions such as equals()
, hashCode()
, and toString()
. However, they can still be accessed and used like any other property.
For example, consider the following data class:
data class Person(val name: String) {
var age: Int = 0
}
In this example, name
is a property of the primary constructor and age
is a property declared in the class body. When comparing two Person
objects using the ==
operator, only the name
property will be considered.
val person1 = Person("John")
val person2 = Person("John")
person1.age = 10
person2.age = 20
println("person1 == person2: ${person1 == person2}")
This will result in person1 == person2: true
, even though the age
property is different between the two objects.
In addition, the toString()
, hashCode()
, and copy()
functions will also only include the name
property. If we wish to include additional properties in these functions, we can override them in the class body.
It's important to keep in mind that properties declared in the class body are not considered part of the data class' signature, and thus will not affect the automatically generated functions. They can be used for additional functionality and can be used to store additional data, but it's important to consider the trade-offs.
Copy()
In Kotlin, the copy()
function is a convenient way to create a new instance of a data class while altering some or all of its properties. The copy()
function is generated automatically by the compiler for data classes and takes all properties of the class as its parameters. The values passed to the copy()
function will be used to create a new instance of the class, while the values that are not passed will be taken from the original instance.
Here is an example of using the copy()
function in a data class:
data class Person(val name: String, val age: Int)
fun main() {
val person1 = Person("John", 25)
val person2 = person1.copy(age = 30)
println(person1) // Person(name=John, age=25)
println(person2) // Person(name=John, age=30)
}
In this instance, person1
is an instance of the Person
data class, with the name "John" and age 25. Using the copy()
function, we modify the age to 30 and create a new instance of the class, person2
. As we can see, two unique instances with different age values arise from copying the name property from the original instance and setting the age property to the new value.
It's important to keep in mind that the copy()
function creates a new object with the provided properties rather than altering the original object. This makes it a helpful tool for making minor changes to existing instances before creating new ones.
In conclusion, the copy()
function in Kotlin is a simple yet powerful tool for creating new instances of data classes with modified properties. It's easy to use, and it helps to keep your code clean and readable.
Desctructuting decarations with data classes
Data classes in Kotlin come with a set of automatically generated functions, including component functions. These component functions make it possible to use data classes in destructuring declarations.
A destructuring declaration is a way to decompose an object into its individual properties. It allows you to assign the properties of an object to separate variables in a single line. For example, consider the following data class:
data class Person(val name: String, val age: Int)
We can create an instance of this class and use it in a destructuring declaration as follows:
val jane = Person("Jane", 35)
val (name, age) = jane
println("$name, $age years of age") // prints "Jane, 35 years of age"
In this example, the variables name
and age
are assigned the values of the name
and age
properties of the jane
object, respectively. The variables are created and initialized in a single line, making the code more concise and readable.
It is also possible to use destructuring declarations with a specific component function. For example, if we only need the name property of the object, we can use the component1()
function which is generated for the first property of the data class.
val (name) = jane
println("Name: $name") // prints "Name: Jane"
In summary, destructuring declarations provides a convenient and readable way to work with the properties of data classes. They allow us to easily access and assign the properties of an object without having to reference the object itself. This can make our code more readable and maintainable.
Conclusion
In conclusion, data classes in Kotlin provide a simple and efficient way to define classes that hold data. They allow developers to quickly create classes with minimal boilerplate code and automatically generated functions such as equals()
, hashCode()
, and toString()
. They also provide a convenient way to copy objects and use them in destructuring declarations.
In the coming article, we'll discuss sealed classes. See you then.
Happy Hacking!