Introduction: Why Doesn't a Data Class Want to Be Empty?
\n\nHave you ever tried to create a data class in Kotlin for Android and were surprised to find that the compiler refuses to accept an empty constructor? If so, you are not alone. This question has gathered thousands of views on Stack Overflow, meaning hundreds of developers (from beginners to experienced) have stumbled over the same rake.
Why does this happen? The thing is, a data class in Kotlin is not just a class for storing data. It automatically generates equals(), hashCode(), toString(), copy(), and componentN() functions. And for all this magic, it needs a primary constructor with at least one parameter. Without it, it's impossible.
In this article, we will break down the top 5 mistakes developers make when trying to create an empty constructor for a data class in Kotlin Android and show you how to avoid them. Let's go!
Mistake #1: Directly Declaring an Empty Constructor
\n\nThe most common mistake is trying to write something like:
\n\n// ❌ This is not allowed!\ndata class User {\n constructor() : this() // Compilation error\n}\n\n\nKotlin strictly requires that the primary constructor be declared in the class header. If you try to create a secondary constructor without parameters that calls itself, you will get infinite recursion and a compilation error.
\n\nSolution: Default Values
\n\nThe simplest and most elegant way is to set default values for all parameters of the primary constructor:
\n\n// ✅ Correct: default values\ndata class User(\n val name: String = "",\n val age: Int = 0,\n val email: String = ""\n)\n\n// Now you can create an object without arguments\nval emptyUser = User()\nprintln(emptyUser) // User(name=, age=0, email=)\n\n\nThis approach works "out of the box" and does not require any additional plugins or annotations. You get a full-fledged empty constructor, and all fields are initialized with default values.
\n\nMistake #2: Ignoring Library Requirements (Room, Retrofit, Gson)
\n\nMany Android libraries require an empty constructor to create objects via reflection. A classic example is Room:
\n\n// ❌ Room will throw an error\n@Entity\ndata class User(\n @PrimaryKey val id: Int,\n val name: String,\n val age: Int\n)\n\n\nWhen trying to use such a data class with Room, you will get an exception: Cannot instantiate User using no-arg constructor. Similar problems arise with Gson and Retrofit when deserializing JSON.
Solution: Default Values + Nullable Types
\n\nFor Room, it's enough to add default values:
\n\n// ✅ Room is happy\n@Entity\ndata class User(\n @PrimaryKey val id: Int = 0,\n val name: String = "",\n val age: Int = 0\n)\n\n\nFor Gson and Retrofit, it's often easier to use nullable types:
\n\n// ✅ For working with JSON\ndata class ApiResponse(\n val status: String? = null,\n val data: List<Item>? = null\n)\n\n\nBut be careful: nullable types can lead to NPE if you forget to check the value. Use them consciously.
\n\nMistake #3: Forgotten Kotlin No-Arg Plugin
\n\nSometimes default values are not suitable. For example, if you are using Jackson for serialization and you need specific types without nullable. In this case, the kotlin-noarg plugin comes to the rescue.
Many developers mistakenly think that simply adding a dependency to build.gradle is enough. But without proper configuration, the plugin will not work.
Solution: Correct No-Arg Configuration
\n\nAdd the plugin to build.gradle.kt: