ProgrammingKotlin Developer

How do Data Objects work in Kotlin, what are they used for, how are equals/hashCode/toString implemented, and how do they differ from regular object and data class?

Pass interviews with Hintsage AI assistant

Answer.

Background:

Before Kotlin 1.9, object objects could not be data - you could not have a singleton that automatically gets equals, hashCode, toString like data classes. With the introduction of data object, this restriction has been lifted. Now you can create a singleton object with auto-generated methods, suitable for Value and Enum-like patterns.

Problem:

Previously, to get correct equals(), hashCode(), toString() even for singleton objects, you had to manually implement them or use other tricks, which increased boilerplate code and the possibility of errors.

Solution:

Use data object for objects whose instance is unique, while needing standard behavior for equals/hashCode/toString for use in collections, serialization, comparison, and debugging.

Code example:

data object NotAvailable fun checkStatus(status: Any) = when (status) { NotAvailable -> "Data is missing" else -> "Other status" } val set = setOf(NotAvailable) println(NotAvailable in set) // true println(NotAvailable.toString()) // NotAvailable

Key features:

  • Data object implements equals/hashCode/toString based on the contract of data class
  • It is a singleton object (only one instance)
  • Suitable for value-like or enum-like patterns without data

Trick questions.

Can data objects contain properties?

They can, but only val properties without backing fields (since nothing stored should be in a singleton)

data object Loading { val status: String get() = "Loading..." }

How does data object differ from a regular object in terms of equals?

Equals for a regular object checks only reference identity, while data object compares the data contract, but in the case of singleton, it is always the same object. However, overridden equals/hashCode is more useful for collections.

Can you inherit from data object?

No, data objects are final, just like any object in Kotlin - inheritance is not allowed.

Common mistakes and anti-patterns

  • Using data object instead of enum when there are multiple objects
  • Using data object trying to store data - not allowed
  • Not considering that equality is always reference-based, but collections expect value-based equality

Real-life example

Negative case

Instead of using an enum for all states, different data objects were used. A year later, serialization by names became necessary, and manual mapping of object names to types was needed.

Pros:

  • Easy initialization

Cons:

  • Extra hacks for serialization, mapping error

Positive case

For the return value of a network request, data objects were used for special statuses: Loading, Empty, Error. This makes the code compact, with support for equals, hashCode, toString automatically.

Pros:

  • Convenient for checking in collections
  • Nice logging

Cons:

  • Cannot add variable properties, only val-lazy