You are currently viewing Kotlin Data class

Kotlin Data class

What is a data class in Kotlin?

A data class is a special type of class that is primarily used to represent data or hold data. Kotlin compile automatically generates several standard functions often required for data classes, such as equals(), hashCode(), toString(), and copy(). Data classes are a convenient way to model immutable data in a concise and readable manner. Data classes are marked with data:

Here’s a simple example of a data class:

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

In the example above, the Person class is marked as a data class. The primary constructor parameters name and age. Kotlin compiler automatically generates the following members from all properties declared in the primary constructor, such as equals(), hashCode(), toString(), and copy().

To create a data class in Kotlin, the following conditions should be fulfilled:

1. Primary Constructor:

A data class must have a primary constructor with at least one parameter.

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

2. No Abstract, Open, Sealed, or Inner Classes:

A data class cannot be abstract, open, sealed, or an inner class. It must be a regular class.

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

// Incorrect - Abstract data class
abstract data class Person(val name: String, val age: Int)

3. Primary Constructor Parameters:

All primary constructor parameters must be marked as val or var. This is necessary because the generated methods (toString(), equals(), hashCode(), and copy()) rely on the properties being declared with either val (immutable) or var (mutable).

data class Person(val name: String, var age: Int)
Read : Want to know about how Kotlin constructors work? Explore Kotlin Constructors

Properties declared in the class body

A data class can also have properties declared in the class body. These properties will not be considered as part of the generated methods (e.g., toString(), equals(), hashCode(), copy()). The generated methods only take into account the properties declared in the primary constructor. To exclude a property from the generated implementations, declare it inside the class body:

data class Person(val name: String, var age: Int) {
    // Property declared in the class body
    var city: String = "Unknown"
}

In this example, city is a property declared in the class body. While it is a valid part of the Person class, it won’t be included in the generated methods.

data class Person(val name: String, var age: Int,) {
    // Property declared in the class body
    var city: String = "Unknown"
}

fun main() {
    val person1 = Person("Alice", 25,)
    val person2 = Person("Alice", 25)
    person1.city = "Dominica"
    person1.city = "Portsmouth"

    println(person1 == person2)  // true
    println(person1.hashCode()) //1963861433
 Same hashCode
    println(person2.hashCode()) //1963861433

    // The 'city' property is not considered in the generated methods

}

If you want a property to be considered in the generated methods, it should be declared in the primary constructor:

data class Person(val name: String, var age: Int, val city: String = "Unknown")

Now, the city property will be part of the generated methods, and its value will be used for equality comparisons, hashing, etc.

Read : "Learn the basics of Kotlin classes in our blog: Kotlin Classes Simplified"

Copying in data class?

The copy() method is automatically generated, allows to create a new instance of the data class with some or all of its properties changed. This is particularly useful for creating modified copies of objects while keeping the original unchanged. Use .copy() when want to change some of properties of object , rest properties are unchanged. Let’s see what would be the implementation of copy function for the Person class :

    fun copy(name: String = this.name, age: Int = this.age) = Person(name, age)

Common use cases for the copy function with example:

1.Updating Properties:

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

val originalPerson = Person("Alice", 25)
val olderPerson = originalPerson.copy(age = 26)

// originalPerson remains unchanged
println(originalPerson)  // Person(name=Alice, age=25)

// olderPerson is a modified copy with the age updated
println(olderPerson)     // Person(name=Alice, age=26)

In the example, originalPerson.copy(age = 26) creates a new instance (olderPerson) with the age updated while keeping the original instance (originalPerson) unchanged.

2. Cloning for Mutation:

data class MutablePerson(var name: String, var age: Int)

val originalMutablePerson = MutablePerson("Bob", 30)
val mutatedPerson = originalMutablePerson.copy()

// mutating the copied instance doesn't affect the original
mutatedPerson.name = "Robert"

println(originalMutablePerson)  // MutablePerson(name=Bob, age=30)
println(mutatedPerson)          // MutablePerson(name=Robert, age=30)

In the example, originalMutablePerson.copy() creates a new instance (mutatedPerson) with the same values, and changes to mutatedPerson do not affect the original instance (originalMutablePerson).

3. Immutable Data Structures:

data class ImmutableList(val values: List<String>)

val originalList = ImmutableList(listOf("A", "B", "C"))
val updatedList = originalList.copy(values = originalList.values + "D")

// originalList remains unchanged
println(originalList)  // ImmutableList(values=[A, B, C])

// updatedList is a modified copy with an additional element
println(updatedList)   // ImmutableList(values=[A, B, C, D])

In the example, originalList.copy(values = originalList.values + "D") creates a new instance (updatedList) with an additional element “D” while keeping the original list unchanged.

Destructuring declarations

Destructuring declarations in Kotlin allow you to break down an object into its individual components and assign them to variables. This feature works particularly well with data classes because the structure of the class aligns with the concept of destructuring.

Here’s an example of destructuring a Person object:

fun main() {
    val person = Person("Alice", 25)

    // Destructuring declaration
    val (name, age) = person

    // Using the variables
    println("Name: $name, Age: $age")// Name: Alice, Age: 25
}

In this example, the person object is destructured into name and age. Now, you can use these variables directly, making the code more concise and readable.

What is use cases for data classes in Kotlin Android development?

In Android development with Kotlin, data classes are commonly used to model and represent data entities, such as objects retrieved from a database, network responses, or UI elements. Here are some common use cases for data classes in Kotlin Android development:

1. Modeling Data Entities:

Data classes are ideal for representing entities in your application, such as user profiles, messages, or any other data structure. Example:

data class User(val id: Int, val username: String, val email: String)

2. Parcelable Implementation:

Android often requires passing data between activities or fragments. Data classes can easily implement the Parcelable interface, simplifying the process of passing data through intents or bundles. Example:

data class ParcelableUser(val id: Int, val username: String, val email: String) : Parcelable

3. Network Responses:

When making network requests, data classes can represent the structure of the JSON responses. Libraries like Retrofit can automatically deserialize JSON responses into data class instances. Example:

data class ApiResponse(val success: Boolean, val message: String, val data: SomeData)

4. Database Entities:

When working with Room or other database libraries, data classes can represent entities stored in the database. The structure of the data class aligns well with the table schema. Example:

@Entity(tableName = "users")
data class DbUser(
    @PrimaryKey val id: Int,
    @ColumnInfo(name = "username") val username: String,
    @ColumnInfo(name = "email") val email: String
)

5. UI State Representation:

Data classes can be used to represent the state of UI elements, making it easy to manage and update the UI based on changes in the data. Example:

data class ViewState(val isLoading: Boolean, val data: List<Item>, val error: String?)

6. Event Handling:

Data classes can be employed to represent events or actions within the app, making it easier to communicate and handle different states or user interactions. Example:

data class ButtonClickEvent(val buttonId: Int)

To learn more about data classes in Kotlin, visit the official Kotlin documentation at: Kotlin Data Classes

Also Read :

Leave a Reply