Question
In Kotlin for Android, I have a data class with many properties, and I want to create an object without passing every constructor argument, similar to how an empty constructor is often used in a Java POJO.
Here is the data class:
data class Activity(
var updated_on: String,
var tags: List<String>,
var description: String,
var user_id: List<Int>,
var status_id: Int,
var title: String,
var created_at: String,
var data: HashMap<*, *>,
var id: Int,
var counts: LinkedTreeMap<*, *>,
)
I would like to use it like this:
val activity = Activity()
activity.title = "New Computer"
sendToServer(activity)
But right now, Kotlin requires all constructor arguments to be passed when creating the object, so I end up doing this:
val activity = Activity(
null,
null,
null,
null,
null,
"New Computer",
null,
null,
null,
null
)
sendToServer(activity)
How can this be simplified in Kotlin?
Short Answer
By the end of this page, you will understand how Kotlin handles constructors in data classes, why all primary constructor values are required by default, and how to make object creation easier using default parameter values and nullable types when needed. You will also see cleaner alternatives such as named arguments and learn when an "empty constructor" is appropriate in Kotlin.
Concept
In Kotlin, a data class is designed to hold data and usually defines its properties directly in the primary constructor. If a constructor parameter has no default value, Kotlin requires you to provide it every time you create an object.
That is why this fails:
val activity = Activity()
Your class currently says that every property is required:
data class Activity(
var updated_on: String,
var tags: List<String>,
var description: String,
var user_id: List<Int>,
var status_id: Int,
var title: String,
var created_at: String,
var data: HashMap<*, *>,
var id: Int,
var counts: LinkedTreeMap<*, *>
)
Kotlin's usual solution is not to add a Java-style empty constructor manually. Instead, you give properties default values in the primary constructor. Then Kotlin can generate a no-argument style call like Activity().
Example:
data class Activity(
var updated_on: String = "",
var tags: List<String> = emptyList(),
var description: String = "",
var user_id: List<Int> = emptyList(),
var status_id: Int = 0,
var title: String = "",
var created_at: String = "",
var data: HashMap<*, *> = hashMapOf<Any?, Any?>(),
var id: Int = 0,
var counts: LinkedTreeMap<*, *> = LinkedTreeMap<Any?, Any?>()
)
Now this works:
val activity = Activity()
activity.title = "New Computer"
If a property is allowed to be missing, you can also make it nullable:
var description: String? = null
This matters in real programming because Kotlin encourages you to model data clearly:
- Required values stay non-null and required.
- Optional values get defaults or become nullable.
- Object creation becomes safer and easier to read.
This leads to cleaner Android code, safer API models, and fewer bugs caused by accidental null values.
Mental Model
Think of a Kotlin data class constructor like a form you must fill out.
- If every field is marked required, you must complete them all before submitting.
- If some fields have pre-filled default values, you can leave them alone and only change what matters.
- If some fields are nullable, it means "this field can be left blank."
So instead of building a Java-style empty object first and filling it later, Kotlin prefers one of these approaches:
- create an object with sensible defaults
- provide only the values you need
- explicitly mark optional fields as nullable
That makes your data model more honest and easier to reason about.
Syntax and Examples
Core syntax
Use default parameter values in the primary constructor:
data class Activity(
var updatedOn: String = "",
var tags: List<String> = emptyList(),
var description: String = "",
var userIds: List<Int> = emptyList(),
var statusId: Int = 0,
var title: String = "",
var createdAt: String = "",
var data: HashMap<String, Any> = hashMapOf(),
var id: Int = 0
)
Now you can create it with no arguments:
val activity = Activity()
activity.title = "New Computer"
Using named arguments
Kotlin also lets you set only what you care about at creation time:
val activity = Activity(title = "New Computer")
This is usually cleaner than creating an empty object and mutating it later.
Using nullable properties when values may be absent
If a field may genuinely be missing, make it nullable:
data class Activity(
var updatedOn: String? = null,
var tags: List<String> = emptyList(),
var description: String? = null,
var userIds: List<Int> = emptyList(),
var statusId: Int = 0,
var title: String = "",
var createdAt: String? = null,
var data: HashMap<String, Any> = hashMapOf(),
var id: Int = 0
)
Better version of your example
data class Activity(
var updated_on: String? = null,
var tags: List<String> = emptyList(),
var description: String? = null,
var user_id: List<Int> = emptyList(),
var status_id: Int = 0,
var title: String = "",
var created_at: String? = null,
var data: HashMap<Any?, Any?> = hashMapOf(),
var id: Int = 0,
var counts: LinkedTreeMap<Any?, Any?> = LinkedTreeMap()
)
Usage:
val activity = Activity(title = "New Computer")
sendToServer(activity)
This is concise, readable, and idiomatic Kotlin.
Step by Step Execution
Consider this example:
data class Activity(
var title: String = "",
var statusId: Int = 0,
var description: String? = null
)
val activity = Activity(title = "New Computer")
What happens step by step
-
Kotlin sees the
Activityconstructor has default values for all properties. -
You call:
Activity(title = "New Computer") -
Kotlin assigns
titleto"New Computer". -
Kotlin uses the default value
0forstatusId. -
Kotlin uses the default value
nullfordescription. -
The final object becomes:
Activity(title="New Computer", statusId=0, description=null)
Another trace
val activity = Activity()
activity.title = "New Computer"
Step by step:
Activity()creates an object using all default values.- So initially it is:
Activity(title="", statusId=0, description=null)
- Then
activity.title = "New Computer"updates thetitleproperty. - The object is now:
Activity(title="New Computer", statusId=0, description=null)
This works only because every constructor parameter had a default value.
Real World Use Cases
Default constructor-like behavior in Kotlin is useful in many practical situations:
- Form models in Android apps
- Start with empty values and fill fields as the user types.
- API request bodies
- Create a request object with a few known fields and leave the rest as defaults.
- JSON parsing models
- Some fields may be missing in server responses, so defaults or nullable fields help.
- Local state objects
- UI state in ViewModels often starts with empty strings, empty lists, or
null.
- UI state in ViewModels often starts with empty strings, empty lists, or
- Testing
- Build objects quickly without passing every field each time.
Example in an Android ViewModel:
val draft = Activity(title = "New Computer")
repository.sendToServer(draft)
This is much clearer than passing ten null values.
Real Codebase Usage
In real projects, developers usually combine default values with a few common Kotlin patterns.
1. Named arguments
Instead of relying on positional arguments:
val activity = Activity("", emptyList(), "", emptyList(), 0, "New Computer", "", hashMapOf(), 0, LinkedTreeMap())
developers prefer:
val activity = Activity(title = "New Computer")
This is easier to read and safer.
2. Validation before sending data
A model may allow defaults, but the app still validates required fields:
if (activity.title.isBlank()) {
throw IllegalArgumentException("Title is required")
}
3. copy() for updates
Data classes support copy(), which is heavily used in real codebases:
val original = Activity(title = "Draft")
val updated = original.copy(title = "Published")
This is especially common in state management.
4. Safe defaults for collections
Developers usually prefer:
emptyList()instead ofnullemptyMap()orhashMapOf()instead ofnull
This reduces null checks.
5. Separate request/response models
In larger apps, request models are often different from UI models. For example:
ActivityDraftfor user inputActivityRequestfor API submissionActivityResponsefor server data
This avoids one class trying to represent every possible state.
Common Mistakes
1. Using null with non-nullable types
This does not work:
data class Activity(
var title: String,
var id: Int
)
val activity = Activity(null, null)
Why it fails:
Stringis notString?Intis notInt?
Fix it by either:
- making the property nullable, or
- providing a real default value
data class Activity(
var title: String = "",
var id: Int = 0
)
2. Making everything nullable without a reason
Broken design:
data class Activity(
var title: String? = null,
var id: Int? = null
)
This may force null checks everywhere:
if (activity.title != null) {
println(activity.title.length)
}
Prefer non-null properties with sensible defaults when possible.
3. Passing many positional arguments
Hard to read:
val activity = Activity("", emptyList(), "", emptyList(), 0, "New Computer", "", hashMapOf(), 0, LinkedTreeMap())
Better:
val activity = Activity(title = "New Computer")
4. Choosing poor defaults
A default should make sense.
""for text fields may be fineemptyList()is often better thannull0may or may not make sense for IDs depending on your app
5. Ignoring model meaning
If a field is truly required for valid business logic, do not hide that by making it optional unless necessary. Defaults improve convenience, but they should not weaken correctness.
Comparisons
| Approach | How it works | Pros | Cons | Best use |
|---|---|---|---|---|
| Required constructor parameters | Every field must be provided | Strict and explicit | Verbose for large models | When all fields are truly required |
| Default parameter values | Constructor parameters have fallback values | Clean, idiomatic Kotlin, allows Activity() | Poor defaults can hide invalid data | Most common Kotlin solution |
| Nullable properties | Fields can be null | Good for truly optional data | More null checks | When missing values are valid |
| Secondary constructor | Add another constructor manually | Can help interoperability in some cases | Usually unnecessary in Kotlin | Special cases, Java/framework needs |
Default values vs nullable properties
// Default values
data class User(val name: String = "")
// Nullable
data class User(val name: String? = null)
Choose default values when:
- the property should usually exist
- an empty value is acceptable
Choose nullable when:
- the property may genuinely be absent
nullhas a real meaning
Kotlin style vs Java POJO style
Java often uses:
- empty constructor
- setters after construction
Kotlin usually prefers:
- primary constructor
- defaults
- named arguments
- immutable
valproperties when possible
That leads to more predictable objects.
Cheat Sheet
Quick reference
Create a data class with default values
data class Activity(
var title: String = "",
var id: Int = 0,
var tags: List<String> = emptyList()
)
Call it with no arguments
val activity = Activity()
Set only one property using named arguments
val activity = Activity(title = "New Computer")
Make a field optional with null
var description: String? = null
Prefer these defaults
String->""Int->0if valid in your domainList<T>->emptyList()MutableList<T>->mutableListOf()HashMap<K, V>->hashMapOf()
Rules to remember
- No default value => argument is required
- Default value => argument becomes optional
- Non-null type cannot receive
null - Use named arguments to avoid long unreadable constructor calls
- Use
copy()to create modified versions of data class objects
Example
data class Activity(
var title: String = "",
var description: String? = null
)
val a1 = Activity()
val a2 = Activity(title = "New Computer")
val a3 = a2.copy(description = "Office setup")
FAQ
Can a Kotlin data class have an empty constructor?
Yes, effectively. If all constructor parameters have default values, you can call the class with no arguments using Activity().
Should I use null for every field to simulate Java behavior?
Usually no. Prefer sensible default values and only use nullable types when null has a real meaning.
What is the idiomatic Kotlin way to create an object with only one changed field?
Use default values plus named arguments, for example:
val activity = Activity(title = "New Computer")
Can I add a secondary constructor instead?
Yes, but it is usually unnecessary for this case. Default parameter values are the standard Kotlin solution.
Why does Kotlin not allow null for String or Int automatically?
Because Kotlin distinguishes between nullable and non-nullable types to reduce null-related bugs.
Is Activity() or Activity(title = "New Computer") better?
Activity(title = "New Computer") is usually better because it expresses intent clearly at creation time.
Should data class properties be var or val?
Use val when the value should not change after creation. Use var only when mutation is necessary.
Mini Project
Description
Build a small Kotlin model for creating and sending a draft support ticket. The goal is to practice defining a data class with default values so you can create objects without passing every field manually. This mirrors real Android and backend request models where some fields start empty and only a few are filled before submission.
Goal
Create a Kotlin data class that can be instantiated with no arguments or with only selected fields using named arguments.
Requirements
- Define a
data classfor a support ticket with at least five properties. - Give every property a sensible default value.
- Create one empty ticket and update one field after creation.
- Create another ticket using named arguments.
- Print both tickets to verify the values.
Keep learning
Related questions
Accessing Kotlin Extension Functions from Java
Learn how Kotlin extension functions are compiled and how to call them correctly from Java with clear examples and common pitfalls.
Add a Maven Repository URL in Gradle Kotlin DSL (build.gradle.kts)
Learn how to add Maven Central and a custom Maven repository URL in Gradle Kotlin DSL using build.gradle.kts syntax.
Allow HTTP and HTTPS in Android 9 Pie with Network Security Configuration
Learn how Android 9 Pie handles cleartext HTTP traffic and how to allow HTTP and HTTPS safely using network security config.