Data classes and compiler safety

When adding a new property to an existing data class, the compiler can warn us when we forget to initialize its value somewhere in the codebase. But it can only help if the new property does not have a default value: without the help from the compiler, it is very easy to miss an unset value, particularly in large code bases! Going through all the places where the property is used also gives us a valuable chance to revisit the business logic and think about why and where it is used.

Defaults are better suited for @Preview functions and tests. It would be frustrating if every test and preview, however unrelated, required modification just because of a new property in a data class and relevant tests are going to fail anyway. We can make use of default values to satisfy our “laziness” and save on typing while still retaining compiler safety by lifting the defaults out of the constructor.

data class Settings(
    val itemsPerPage: Int,
    val confirmExit: Boolean,
) {
    companion object
}

val Settings.Companion.Default
    get() = Settings(
        itemsPerPage = 25,
        confirmExit = false,
    )

Please note in the code above that the default object isn’t kept in memory at all times, but a new one is created everytime the getter of the property is called.

This approach borrows from the Default trait in Rust and the Extension-oriented design pattern.

Copyright © 2026 Károly Kiripolszky