Sitemap

Data Class

6 min readNov 27, 2024

--

Kotlin Data Class

Data class is a special type of class in Kotlin used to hold the data. A data class provides several useful features/method automatically, which reduces boilerplate code for commonly used operations like equals, hashCode, toString, and copy.

When you declare a data class in Kotlin, the compiler generates several standard methods and features behind the scenes based on the properties in the primary constructor. Here’s an explanation of how these work “under the hood”:

Code Transformation Process

1. Example of Data class

data class Book(val title: String, val author: String, val price: Double)

2. The Kotlin compiler generates the following behind the scenes:

The Kotlin compiler generates the following behind the scenes. When the Kotlin code is compiled to JVM bytecode, the compiler translates the data class into a standard Java class with the corresponding implementations of equals, hashCode, and other methods.

public final class Book {
private final String title;
private final String author;
private final double price;

// Constructor
public Book(String title, String author, double price) {
this.title = title;
this.author = author;
this.price = price;
}

// Getters (no setters, as properties are immutable by default)
public String getTitle() {
return title;
}

public String getAuthor() {
return author;
}

public double getPrice() {
return price;
}

// equals() method
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
Book book = (Book) o;
return Double.compare(book.price, price) == 0 &&
title.equals(book.title) &&
author.equals(book.author);
}

// hashCode() method
@Override
public int hashCode() {
return Objects.hash(title, author, price);
}

// toString() method
@Override
public String toString() {
return "Book(title=" + title + ", author=" + author + ", price=" + price + ")";
}

// copy() method (custom-generated for data classes)
public Book copy(String title, String author, double price) {
return new Book(
title == null ? this.title : title,
author == null ? this.author : author,
price == 0.0 ? this.price : price
);
}

// Component functions for destructuring
public String component1() {
return title;
}

public String component2() {
return author;
}

public double component3() {
return price;
}
}

3. How to use


fun main() {
// Creating a data class object
val book1 = Book("Kotlin for Beginners", "John Doe", 29.99)

// toString() example
println(book1) // Output: Book(title=Kotlin for Beginners, author=John Doe, price=29.99)

// equals() and hashCode() example
val book2 = Book("Kotlin for Beginners", "John Doe", 29.99)
println(book1 == book2) // Output: true

// copy() example
val book3 = book1.copy(price = 25.99)
println(book3) // Output: Book(title=Kotlin for Beginners, author=John Doe, price=25.99)

// Destructuring declaration
val (title, author, price) = book1
println("Title: $title, Author: $author, Price: $$price")
}

4. When you define a data class, Kotlin automatically generates the following method:

  1. equals(): Compares two objects for structural equality. The equals() method is overridden to compare objects structurally rather than by reference.
  2. hashCode(): Generates a hash code for the object, based on its properties. The hashCode() method generates a unique integer hash based on the object's properties. This is crucial for storing objects in hash-based collections like HashMap or HashSet.
  3. toString(): Provides a readable string representation of the object, e.g., User(name=John, age=25).
  4. copy(): Creates a copy of the object with optional modifications to its properties. The copy() function creates a new object with the same values, allowing optional modification of properties. This is especially useful for immutable objects.
  5. Destructuring declarations: Allows you to unpack the object into its individual properties.

Why Use Data Classes?

  1. Reduces Boilerplate Code: You don’t have to manually write common methods like toString(), equals(), etc.
  2. Convenient for Immutable Objects: Works well when modeling immutable data objects.
  3. Useful for Collections: Easily store and compare data in collections like List or Set.
  4. Improved Readability: Makes the code cleaner and easier to understand.

Important Point or Limitations

  1. Data keyword is used to declare a class as a data class
  2. A data class must have at least one primary constructor parameter.
  3. All primary constructor parameters must be marked as val or var.
  4. A data class can implement one or more interfaces, and it must provide implementations for any abstract methods in those interfaces.
  5. A data class can extend other classes (Abstract or Normal class), and it must provide implementations for any abstract methods in those Abstract or normal class.
  6. Data classes cannot be abstract, open, sealed, or inner.
  7. Data class cannot extent others Data class

Get Abhishek Rai’s stories in your inbox

Join Medium for free to get updates from this writer.

//Data class Declaration:
data class Book(var title: String, var author: String, var price: Double)


//Data class Declaration with Constructor or have more then one Constructor:
data class Book2(var title: String, var author: String, var price: Double){
constructor ():this("","",0.0)
constructor (title: String):this(title,"",0.0)
}


//Data class can extended others Abstract class and Interface:
data class Book3(var title: String, var author: String, var price: Double): MyAbstractClass(), MyInterface {
override fun doSomething() {
TODO("Not yet implemented")
}

override fun doSomething2() {
TODO("Not yet implemented")
}
}

//Data class can extended others Abstract class and Interface:
data class Book4(var title: String, var author: String, var price: Double): MyClass(), MyInterface {
override fun doSomething2() {
TODO("Not yet implemented")
}
}

/**
//Data class can not extent others Data class:
data class Book5(var title: String, var author: String, var price: Double): Book(title, author, price)


//Data class can not be abstract class:
abstract data class Book6(var title: String, var author: String, var price: Double)


//Class can extent the Data class
class Book7(var title: String, var author: String, var price: Double): Book(title, author, price)

*/


open class MyClass {
constructor (): super()
}

abstract class MyAbstractClass {
public abstract fun doSomething()
}

interface MyInterface {
public abstract fun doSomething2()
}

1. What is a data class in Kotlin?

A data class is a class in Kotlin designed to hold data. It automatically generates utility functions like toString(), equals(), hashCode(), copy(), and provides destructuring capabilities. It is declared with the data keyword.

2. What are the requirements for declaring a data class?

To declare a data class, the following requirements must be met:

  • Class must be declare using Data keyword
  • The primary constructor must have at least one parameter.
  • All primary constructor parameters must be marked as val or var.
  • Data classes cannot be abstract, open, sealed, or inner.

3. What methods are automatically generated for a data class?
Kotlin automatically generates:

  • equals(): For structural equality comparison.
  • hashCode(): For hash-based collections.
  • toString(): For string representation of the object.
  • copy(): To create a new object with modified properties.
  • Destructuring component functions (e.g., component1(), component2(), etc.).

4. What is the purpose of the copy() function in a data class?
The copy() function allows you to create a new instance of a data class by copying the existing object and modifying specific properties if needed.

data class User(val name: String, val age: Int)
val user1 = User("Alice", 25)

val user2 = user1.copy(age = 30) // Create a new User with modified age
println(user2) // Output: User(name=Alice, age=30)

val user3 = user1
user3.age = 40
println(user1) // Output: User(name=Alice, age=40) //user1 will also print the age = 40 becaouse we are not chnaging the refrance only modfing the property
println(user3) // Output: User(name=Alice, age=40)

5. What is the difference between == and === in the context of data classes?

  • == checks for structural equality by calling the equals() method (generated for data classes).
  • === checks for referential equality (i.e., whether the two references point to the same object in memory).
val user1 = User("Alice", 25)
val user2 = User("Alice", 25)
println(user1 == user2) // true (structural equality)
println(user1 === user2) // false (different memory references)

6. What happens if a data class has properties that are not part of the primary constructor?
Only properties declared in the primary constructor are included in the generated methods like equals(), hashCode(), and toString(). Other properties must be handled manually if needed.

data class User(val name: String, val age: Int) {
var isOnline: Boolean = false // Not part of equals(), hashCode(), etc.
}

7. How do destructuring declarations work with data classes?
Data classes automatically provide component functions (component1(), component2(), etc.), which allow destructuring declarations to unpack the properties.

val user = User("Alice", 25)
val (name, age) = user
println("Name: $name, Age: $age") // Output: Name: Alice, Age: 25

8. Can you override the toString(), equals(), or hashCode() methods in a data class?
Yes, you can override these methods if you need custom behavior. However, the default implementation provided by Kotlin usually suffices.

data class User(val name: String, val age: Int) {
override fun toString(): String {
return "User's Name: $name, Age: $age"
}
}

9. What are the limitations of data classes?

  • A data class must have at least one primary constructor parameter.
  • All primary constructor parameters must be marked as val or var.
  • Data classes cannot be abstract, open, sealed, or inner.
  • Normal or others Data class cannot extent Data class

--

--