Kotlin
Android Development
Data Class
Constructor
Programming Tutorial

How to create empty constructor for data class in Kotlin Android

Master System Design with Codemia

Enhance your system design skills with over 120 practice problems, detailed solutions, and hands-on exercises.

Introduction

Kotlin data classes require all primary constructor parameters to be initialized, so they do not have a no-argument constructor by default. This causes issues with libraries like Firebase, Room, Gson, and Jackson that need to create instances via reflection using an empty constructor. The solution is to provide default values for all parameters, use the no-arg compiler plugin, or switch to a regular class for complex cases.

The Problem

kotlin
1data class User(val name: String, val age: Int)
2
3// Firebase tries to do this internally:
4val user = User::class.java.newInstance()
5// ERROR: InstantiationException — no zero-argument constructor

Firebase Realtime Database, Firestore, and many JSON parsers create objects by calling the no-arg constructor and then setting fields via reflection. Without a no-arg constructor, deserialization fails.

kotlin
1data class User(
2    val name: String = "",
3    val age: Int = 0,
4    val email: String = ""
5)
6
7// Now all of these work:
8val user1 = User()                              // name="", age=0, email=""
9val user2 = User(name = "Alice")                // age=0, email=""
10val user3 = User("Alice", 30, "[email protected]") // All specified

When every parameter has a default value, Kotlin generates a no-arg constructor automatically. Firebase, Gson, and other reflection-based libraries can instantiate the class without arguments.

Choosing Appropriate Defaults

kotlin
1data class Product(
2    val id: String = "",
3    val name: String = "",
4    val price: Double = 0.0,
5    val inStock: Boolean = false,
6    val tags: List<String> = emptyList(),
7    val metadata: Map<String, Any> = emptyMap(),
8    val createdAt: Long = System.currentTimeMillis()
9)
TypeTypical Default
String""
Int, Long, Double0, 0L, 0.0
Booleanfalse
ListemptyList()
MapemptyMap()
Nullablenull

Solution 2: Nullable Types with null Default

kotlin
1data class User(
2    val name: String? = null,
3    val age: Int? = null,
4    val email: String? = null
5)
6
7val user = User()  // All fields are null

This approach makes it clear that fields may not be set. It works well when you need to distinguish "not provided" from "empty string," but forces null checks everywhere you use the fields.

Solution 3: Secondary Constructor

kotlin
1data class User(val name: String, val age: Int) {
2    // Secondary no-arg constructor delegates to primary
3    constructor() : this("", 0)
4}
5
6val user = User()        // name="", age=0
7val user2 = User("Alice", 30)

This works but has a drawback: the secondary constructor is not used by copy() or destructuring. Default values in the primary constructor are usually cleaner.

Solution 4: no-arg Compiler Plugin

The Kotlin no-arg plugin generates a synthetic no-arg constructor for annotated classes without changing your source code:

groovy
1// build.gradle.kts
2plugins {
3    kotlin("plugin.noarg") version "1.9.0"
4}
5
6noArg {
7    annotation("com.example.NoArg")
8}
kotlin
1// Define the annotation
2annotation class NoArg
3
4// Apply it to your data class
5@NoArg
6data class User(val name: String, val age: Int)
7
8// Firebase/reflection can now create User() even though
9// no default values are specified in the source code

The generated constructor is only visible to reflection — you cannot call User() from Kotlin code directly. This is the cleanest approach when you want immutable val properties without fake defaults.

Firebase-Specific Pattern

kotlin
1import com.google.firebase.firestore.DocumentId
2
3data class User(
4    @DocumentId val id: String = "",
5    val name: String = "",
6    val age: Int = 0,
7    val email: String = ""
8)
9
10// Read from Firestore
11val user = document.toObject(User::class.java)
12
13// Write to Firestore
14firestore.collection("users").add(User(name = "Alice", age = 30))

Firebase requires:

  1. A no-arg constructor (provided by default values)
  2. Properties must be var or have matching setter methods — but Firestore actually works with val by using the constructor parameters directly if they match field names

Room Database Pattern

kotlin
1import androidx.room.Entity
2import androidx.room.PrimaryKey
3
4@Entity(tableName = "users")
5data class User(
6    @PrimaryKey(autoGenerate = true) val id: Int = 0,
7    val name: String = "",
8    val age: Int = 0
9)

Room uses the primary constructor directly and matches column names to parameter names. Default values ensure Room can create instances during query result mapping.

Gson and Moshi

kotlin
1// Gson works with no-arg constructor + reflection
2data class ApiResponse(
3    val status: String = "",
4    val data: List<Item> = emptyList(),
5    val error: String? = null
6)
7
8// Moshi with codegen (no no-arg constructor needed)
9@JsonClass(generateAdapter = true)
10data class ApiResponse(
11    val status: String,
12    val data: List<Item>,
13    val error: String?
14)
15// Moshi codegen reads the constructor directly — no reflection, no empty constructor needed

Common Pitfalls

  • Using var unnecessarily: Many developers switch to var for Firebase compatibility, but val with default values works in most cases. Prefer val for immutability.
  • Forgetting one parameter's default: If even one parameter lacks a default, no no-arg constructor is generated. All parameters must have defaults for the zero-argument form to exist.
  • Empty string vs null ambiguity: Defaulting to "" makes it impossible to distinguish "field was empty" from "field was never set." Use nullable types (String? = null) when this distinction matters.
  • ProGuard/R8 stripping constructors: Minification can remove the generated no-arg constructor. Add keep rules for data classes used with reflection: -keep class com.example.models.** { *; }.
  • Assuming copy() uses secondary constructors: The copy() function always uses the primary constructor. Default values in the primary constructor are preserved; secondary constructor defaults are not used by copy().

Summary

  • Give all primary constructor parameters default values to get a free no-arg constructor
  • Use "", 0, false, emptyList(), or null as defaults depending on the type
  • The no-arg compiler plugin generates a reflection-only constructor without changing source code
  • Firebase, Room, Gson, and most reflection-based libraries work when default values are provided
  • Prefer val properties with defaults over var — immutability is a Kotlin strength

Course illustration
Course illustration

All Rights Reserved.