Every Kotlin class needs a way to set up its initial state. That’s exactly what Kotlin constructors do, and they work very differently from what Java developers expect.
Kotlin splits object initialization into primary constructors, secondary constructors, and init blocks. Each one has specific rules about where it goes, when it runs, and what it can access. Getting these wrong leads to bugs that compile fine but break at runtime.
This article covers how each constructor type works, how they interact with class properties, inheritance, and visibility modifiers. You’ll also see how data class constructors differ from regular ones, common mistakes developers make during Java migration, and practical patterns like factory methods with companion objects.
What Is a Constructor in Kotlin

A constructor in Kotlin is a special function that initializes an object when you create a new instance of a class. Every time you write val user = User("Alice", 25), a constructor runs behind the scenes to set up that object’s state.
But here’s where Kotlin breaks from what Java developers expect.
Kotlin splits constructors into two categories: primary constructors and secondary constructors. The primary constructor lives right in the class header. Secondary constructors sit inside the class body. Most of the time, you’ll only need the primary one.
There’s also the init block, which acts as the primary constructor’s body. Since you can’t write executable code directly inside a primary constructor declaration, init blocks give you a place to run validation logic, logging, or any setup work that goes beyond simple property assignment.
Google declared Kotlin the preferred language for Android development back in 2017. According to JetBrains’ analysis, 95% of the top 1,000 Android apps now include Kotlin code. Understanding how constructors work is one of the first things you need to get right when picking up the language.
If you’re coming from Java, the mental shift is real. Java constructors are method-like blocks inside the class body with the same name as the class. Kotlin’s primary constructor is part of the class declaration itself. Fewer lines, tighter syntax, less room for mistakes.
How Kotlin Constructors Differ from Java
| Feature | Kotlin | Java |
|---|---|---|
| Primary constructor location | Class header | Inside class body |
| Constructor body | init block | Constructor method |
| Default parameter values | Built-in | Requires overloading |
| Property declaration in constructor | val/var in parameters | Separate field + assignment |
| Boilerplate for simple classes | Minimal (1 line possible) | Verbose |
The Stack Overflow 2024 Developer Survey ranked Kotlin as the 4th most admired programming language with 58.2% developer satisfaction. That kind of approval doesn’t happen by accident. The constructor syntax is a big part of why developers prefer Kotlin over more verbose alternatives.
Primary Constructor Syntax and Behavior

The primary constructor is declared directly in the class header, right after the class name. This is probably the single biggest syntax difference between Kotlin and Java when it comes to class initialization.
Here’s what a basic primary constructor looks like:
“ class User(val name: String, val age: Int) `
That single line declares a class with two properties and a constructor. In Java, the same thing takes a class declaration, two private fields, a constructor method, assignments inside the constructor body, and getter methods. Easily 15+ lines.
You can skip the constructor keyword entirely when there are no annotations or visibility modifiers on the primary constructor. Most of the time, you'll leave it out.
Parameters with val or var become class properties automatically. Parameters declared without either keyword are just constructor arguments. They’re accessible inside init blocks and property initializers, but they don't stick around as properties on the object. Took me a while to internalize that distinction, and I've seen plenty of others trip over it too.
Default parameter values work right in the primary constructor:
` class User(val name: String, val age: Int = 0, val email: String = "") `
This pattern alone kills the need for multiple overloaded constructors. The JetBrains 2024 Developer Ecosystem survey found that 75% of Kotlin users express satisfaction with the language, and the concise constructor syntax is a big contributor.
The init Block and Execution Order
Since you can’t put code directly inside the primary constructor, init blocks exist to fill that gap. They run immediately after the primary constructor, in the order they appear in the class body.
And yes, you can have multiple init blocks. They execute sequentially, interleaved with property initializers.
Execution order matters here. The Kotlin compiler processes property initializers and init blocks in the exact order they appear in the source code. Baeldung's analysis of the generated bytecode confirms that the JVM folds all initialization logic into a single constructor method.
Here’s the tricky part. If you reference a property in an init block that's declared below it in the class body, you'll get a null value (or zero for primitives) because that property hasn't been initialized yet. Your code compiles fine. No warnings. It just breaks at runtime.
A practical rule: keep your init blocks after all property declarations if you want to avoid surprises. Or at least be extremely deliberate about the ordering.
Secondary Constructors in Kotlin
Secondary constructors are declared inside the class body using the constructor keyword. They exist mostly for two reasons: Java framework interop and cases where you genuinely need multiple initialization paths.
Every secondary constructor must delegate to the primary constructor using this(). This delegation happens as the very first statement. There's no way around it.
` class User(val name: String, val age: Int) { constructor(name: String) : this(name, 0) { println("Secondary constructor called") } } `
The init blocks and property initializers all run before any code in the secondary constructor body. So by the time your secondary constructor logic executes, the object is already partially initialized through the primary constructor path.
Here’s something worth noting. In practice, you rarely need secondary constructors in Kotlin. Default parameter values handle most of the cases that would require constructor overloading in Java. The JetBrains Kotlin Census data shows that developers who migrate from Java tend to overuse secondary constructors at first, then gradually replace them with default arguments as they get comfortable with the language.
Netflix, McDonald’s, and Forbes all use Kotlin in production. According to industry data, organizations adopting Kotlin report up to 30% reduction in development and maintenance costs. Cleaner constructor patterns are part of that efficiency gain.
Chaining Multiple Secondary Constructors
Secondary constructors can delegate to each other, not just to the primary constructor. The chain always terminates at the primary constructor eventually.
` class User(val name: String, val age: Int) { constructor(name: String) : this(name, 0) constructor() : this("Unknown") } `
Execution order when chaining:
- Primary constructor runs first
- All init
blocks and property initializers execute (in order of appearance)
- The body of each secondary constructor runs, starting from the one closest to the primary constructor in the delegation chain
If you’re migrating a Java codebase with heavily overloaded constructors, chaining gives you a way to preserve the same API surface. But seriously consider whether default parameters would be cleaner.
Constructor Parameters vs. Class Properties

This is where things get confusing for people who are new to Kotlin. A constructor parameter and a class property are not the same thing, even though they look similar.
With val or var: the parameter becomes a real property on the class. It gets a backing field, it’s accessible from anywhere you have a reference to the object, and it shows up in the generated bytecode as a proper field with getter (and setter, for var).
Without val or var: the parameter is just a constructor argument. It’s accessible inside init blocks and inline property initializers, but nowhere else. Once construction finishes, it's gone.
` class User(val name: String, age: Int) { val birthYear = 2026 - age // works: age is accessible here }
// Later… val user = User(“Alice”, 30) println(user.name) // works: name is a property // println(user.age) // compile error: age is not a property `
I’ve seen this mistake come up in code reviews constantly. Someone writes class Config(host: String, port: Int) without val, then wonders why they can't access config.host outside the class.
The distinction also affects things like data classes, where only properties declared in the primary constructor participate in the auto-generated equals(), hashCode(), and toString() methods. Properties declared in the class body don't count.
According to a JetBrains study, 20% of Kotlin developers are already experimenting with Kotlin Multiplatform solutions. When sharing code across platforms, getting your constructor parameter declarations right becomes even more important because the same class definition runs on Android, iOS, and desktop.
Default and Named Arguments in Constructors
If there’s one feature that makes Kotlin constructors dramatically better than Java’s, it’s default and named arguments. They eliminate the “telescoping constructor” problem almost entirely.
Telescoping constructors are what happens in Java when you need multiple ways to create an object. You end up with three, four, sometimes six constructor overloads that all delegate to each other. It’s messy. Kotlin fixes this with one constructor and default values:
` class HttpClient( val baseUrl: String, val timeout: Int = 30, val retries: Int = 3, val logging: Boolean = false ) `
Now you can create instances in multiple ways without writing extra constructors:
` val client1 = HttpClient("https://api.example.com") val client2 = HttpClient("https://api.example.com", timeout = 60) val client3 = HttpClient("https://api.example.com", logging = true, retries = 5) `
Named arguments make the call site readable. You know exactly what each value means without checking the constructor signature. This matters a lot in Android development where classes like view configurations can have a dozen parameters.
Does this replace the builder pattern? Mostly, yes. At least in my experience. There are still edge cases where builders make sense (conditional construction logic, validation across multiple fields), but for straightforward object creation, default plus named arguments are cleaner.
On the JVM, if all primary constructor parameters have default values, the Kotlin compiler generates a parameterless constructor automatically. This is what makes Kotlin play nicely with frameworks like Jackson, Spring Data JPA, and other tools that create class instances through no-arg constructors.
The JetBrains Developer Ecosystem survey from 2025 showed that Kotlin Multiplatform usage jumped from 7% to 18% in a single year. Default arguments work across all Kotlin targets, making them a reliable pattern whether you’re building for Android, server-side, or shared modules.
Data Class Constructors

Data classes are one of Kotlin’s most popular features, and their constructors have specific rules that regular classes don’t.
Requirements for a data class primary constructor:
- At least one parameter is required
- Every parameter must be marked val
orvar
- The class cannot be abstract, open, sealed, or inner
When you declare a data class, the compiler auto-generates equals(), hashCode(), toString(), copy(), and componentN() functions based on the primary constructor properties. Only primary constructor properties. Anything declared inside the class body gets excluded from those generated methods.
` data class Person(val name: String, val age: Int) { var nickname: String = "" // NOT included in equals/hashCode/toString } `
This catches people off guard. Two Person objects with the same name and age but different nicknames will be considered equal. The toString() output won't show the nickname either.
Adding Secondary Constructors to Data Classes
You can add secondary constructors to data classes, but they must delegate to the primary constructor like any other Kotlin class.
` data class User(val name: String, val email: String) { constructor(name: String) : this(name, "$name@default.com") } `
A cleaner alternative is just using default parameter values in the primary constructor. If all parameters have defaults, the Kotlin compiler generates a no-arg constructor on the JVM, which is exactly what libraries like JPA need.
Forbes reportedly shares over 80% of its business logic across iOS and Android using Kotlin Multiplatform. Data classes are the backbone of shared data layers in these setups. Getting the constructor right determines whether your copy() and equals() methods behave as expected across platforms.
Data Classes and the copy() Function
The copy() function creates a new instance with modified values. It only works with primary constructor parameters.
` val original = User("Alice", "alice@test.com") val updated = original.copy(email = "alice@newdomain.com") `
This is the idiomatic way to handle immutable data updates in Kotlin. No builder needed. In software development projects that follow functional patterns, copy() on data classes replaces a lot of the mutation-based code you'd write in Java.
Constructors in Inheritance and Abstract Classes

Kotlin classes are final by default. You have to mark a class with open before any other class can inherit from it. This one design choice prevents a whole category of bugs that Java developers deal with constantly.
When a derived class has a primary constructor, it must initialize the parent class right there in the class header:
` open class Vehicle(val make: String, val year: Int) class Car(make: String, year: Int, val doors: Int) : Vehicle(make, year) `
Parameters pass up to the parent constructor through the colon syntax. The parent’s init blocks run first, then the child's property initializers and init blocks execute in order.
Abstract classes follow the same pattern but can’t be instantiated directly. They can still have constructors, though. Those constructors run when a concrete subclass creates an instance. The Kotlin documentation is clear on this: abstract class constructors exist to enforce required parameters and initialize shared state.
Google Workspace adopted Kotlin Multiplatform to share business logic across Gmail, Docs, and Calendar. The team at KotlinConf 2025 explained how their inheritance hierarchies for shared data models depend on well-structured constructors to guarantee consistent initialization across iOS and Android targets.
Initialization Order Across Parent and Child
Execution sequence when creating a child class instance:
- Parent class primary constructor arguments are evaluated
- Parent class property initializers and init
blocks run (in declaration order)
- Child class property initializers and init
blocks run (in declaration order)
- Secondary constructor body executes (if used)
This order catches people off guard when a parent constructor calls an overridable method. The child’s override will execute before the child’s own properties are initialized. Baeldung’s bytecode analysis confirms this is one of the most common sources of NullPointerException in Kotlin class hierarchies.
Constructor Behavior with Interfaces
Interfaces in Kotlin don’t have constructors. Period.
This means a class implementing multiple interfaces never has to worry about conflicting constructor logic. Only abstract and open classes contribute to the construction chain.
If you need constructor-like setup for an interface-based design, the typical Kotlin pattern is a companion object factory method on the interface itself. The Kotlin standard library uses this approach frequently (look at how Comparator and collection builders work).
Visibility Modifiers on Constructors

By default, every Kotlin constructor is public. Anyone can call it from anywhere. But sometimes you don't want that.
Making a primary constructor private means only the class itself (and its companion object) can create instances. This is the foundation for factory patterns and singleton classes in Kotlin.
There’s one syntax catch. When you add any visibility modifier or annotation to a primary constructor, the constructor keyword becomes mandatory:
` class Database private constructor(val connectionString: String) { companion object { fun create(env: String): Database { return Database("jdbc:postgresql://$env") } } } `
Without the constructor keyword there, the compiler won't know where the modifier belongs. It's a small thing, but it trips up developers who are used to omitting it.
| Modifier | Who Can Call the Constructor | Common Use Case |
|---|---|---|
| public | Anyone, anywhere | Standard classes |
| internal | Same module only | Library-internal types |
| protected | Class and subclasses | Abstract base classes |
| private | Class itself (companion object) | Factories, singletons, DI |
Factory Patterns with Private Constructors
Dependency injection frameworks like Dagger, Hilt, and Koin all interact with constructors in different ways. Hilt uses @Inject annotations on constructors directly. Koin uses factory functions. Both approaches work fine with Kotlin's constructor visibility system.
The “Effective Kotlin” guide by Marcin Moskala recommends companion object factory functions over secondary constructors for most cases. Factory functions can have descriptive names, return cached instances, or return subtypes. Constructors can’t do any of that.
Spring Framework’s Kotlin documentation explicitly shows the private constructor plus companion object pattern for defining beans with static factory methods. The @JvmStatic annotation makes these factory functions visible to Java-based frameworks that expect traditional static methods.
Common Constructor Mistakes and How to Fix Them
These are real problems that come up in code reviews and production debugging. Not theoretical stuff.
A 2025 JetBrains survey found that 37% of developers reported integration issues as their main problem after migrating from Java to Kotlin. Many of those issues trace back to constructor-related misunderstandings.
Forgetting val or var on Constructor Parameters
This is probably the most common beginner mistake. You declare class User(name: String) without val, then try to access user.name somewhere else in your code. The compiler throws an error, and it's not immediately obvious why.
Fix: Always use val unless you specifically need the parameter to be mutable (var) or intentionally want it as a construction-only argument.
Misunderstanding init Block Execution Order
Property initializers and init blocks interleave. If your init block references a property declared below it in the class body, that property hasn't been assigned yet.
This creates a NullPointerException on a non-nullable property, which feels like it shouldn't be possible. According to Kotlin Discussions on the JetBrains forum, this is the second most frequently reported "unexpected behavior" by new Kotlin developers.
Fix: Declare all properties before your first init block.
Leaking this During Construction
Calling an overridable method from a constructor (or init block) lets subclass code run before the subclass is fully initialized. The child's override executes, tries to access its own properties, and gets null values.
IntelliJ IDEA actually warns you about this with a “Calling non-final function in constructor” inspection. Don’t ignore it.
Overusing Secondary Constructors After Migration
The code refactoring path from Java to Kotlin often produces classes with three or four secondary constructors that could be replaced with a single primary constructor using default arguments.
The automatic Java-to-Kotlin converter in IntelliJ IDEA and Android Studio preserves all Java constructor overloads as secondary constructors. That’s correct behavior from the converter, but it’s not idiomatic Kotlin. Manual cleanup is always needed after conversion.
Kotlin Constructors Compared to Java Constructors

If you’ve spent years writing Java, this section is for you. The mapping between Java and Kotlin constructor concepts isn’t one-to-one, and that’s where confusion lives.
According to the Stack Overflow 2024 Developer Survey, almost 92% of Kotlin developers previously used Java. So this comparison matters for the vast majority of people learning Kotlin constructors.
| Concept | Java | Kotlin |
|---|---|---|
| Primary initialization | Constructor method in class body | Parameters in class header |
| Multiple constructors | Overloaded constructor methods | Default arguments (preferred) or secondary constructors |
| Field declaration | Separate from constructor | val/var in constructor = property |
| Initialization code | Inside constructor body | init blocks |
| No-arg constructor | Explicit or compiler-generated | Auto-generated when all params have defaults |
Class Header vs. Constructor Body
Java puts everything inside the class body. Field declarations up top, constructor methods below, assignment statements inside those methods.
Kotlin collapses all of that. A class like class User(val name: String, val age: Int) replaces what would be 15+ lines of Java: two private fields, a constructor method, two assignments, and two getter methods. This reduction in boilerplate is one of the reasons Google's own data shows 70% of the top 1,000 Play Store apps now use Kotlin.
Default Arguments vs. Constructor Overloading
Java approach: Write a constructor with all parameters. Write another with fewer parameters that delegates to the first. Repeat for every combination you need.
Kotlin approach: One constructor. Add = defaultValue to parameters that should be optional. Done.
The @JvmOverloads annotation tells the Kotlin compiler to generate multiple Java-visible constructors from a single Kotlin constructor with default parameters. This is how you maintain backward compatibility when your Kotlin class needs to be called from Java code in the same software development process.
How IntelliJ IDEA Handles Conversion
The built-in converter (Code > Convert Java File to Kotlin File) does a decent job with constructors, but the output is rarely idiomatic.
What the converter does well:
- Maps Java constructor parameters to Kotlin constructor parameters
- Converts field assignments to val
/vardeclarations
- Preserves constructor overloads as secondary constructors
What you’ll need to clean up manually:
- Replace secondary constructors with default parameter values
- Move initialization logic from secondary constructors into init
blocks
- Remove unnecessary explicit types that Kotlin can infer
The code review process after automated conversion should specifically look for these constructor patterns. A quick pass through the converted file usually reveals three or four opportunities to simplify, especially around constructors that were overloaded purely because Java doesn’t support default arguments.
FAQ on What Are Kotlin Constructors
What is the difference between a primary and secondary constructor in Kotlin?
The primary constructor is declared in the class header and handles property declarations directly. Secondary constructors live inside the class body using the constructor keyword. Secondary constructors must always delegate to the primary constructor through this().
Can a Kotlin class have multiple constructors?
Yes. A Kotlin class can have one primary constructor and multiple secondary constructors. But default parameter values usually eliminate the need for overloads. Most idiomatic Kotlin code uses a single primary constructor with defaults instead of chaining several constructors together.
What does the init block do in Kotlin?
The init block runs initialization code right after the primary constructor. Since you can't put executable code directly in a primary constructor, init blocks handle validation, logging, and setup logic. A class can have multiple init blocks, and they execute in declaration order.
Do I need the constructor keyword in Kotlin?
For primary constructors, the constructor keyword is optional unless you add visibility modifiers or annotations. Writing class User private constructor(val name: String) requires it. For secondary constructors inside the class body, the keyword is always mandatory.
What happens if I forget val or var in a Kotlin constructor parameter?
The parameter becomes a construction-time argument only. It won’t be stored as a class property. You can use it inside init blocks and property initializers, but accessing it on the object instance later will cause a compile error.
How do Kotlin constructors differ from Java constructors?
Kotlin’s primary constructor sits in the class header, not inside the body. It supports default arguments natively, which removes the need for overloaded constructors. The init block replaces Java's constructor body. Overall, Kotlin needs far fewer lines for the same result.
Can data classes have custom constructors?
Yes. You can add secondary constructors to a data class, but they must delegate to the primary constructor. The primary constructor needs at least one val or var parameter. Auto-generated methods like equals() and copy() only use primary constructor properties.
How does constructor delegation work in Kotlin inheritance?
A derived class initializes its parent through the class header syntax: class Child(name: String) : Parent(name). The parent's init blocks and property initializers run first. If no primary constructor exists, secondary constructors use super() to call the parent directly.
Can I make a Kotlin constructor private?
Yes. Add private before the constructor keyword in the class header. This restricts instance creation to the class itself and its companion object. It's a common pattern for factory methods, singletons, and controlled object creation in back-end development.
What is the execution order of constructors and init blocks?
Primary constructor runs first. Then property initializers and init blocks execute in the order they appear in the source code. Secondary constructor bodies run last. During inheritance, the parent class completes its entire initialization sequence before the child class begins.
Conclusion
Understanding what are Kotlin constructors comes down to knowing when to use primary constructors, secondary constructors, and init blocks. Each one has a specific job in class initialization, and mixing them up leads to runtime problems that the compiler won’t catch.
Default arguments and named parameters handle most of what Java solves with constructor overloading. That alone cuts significant boilerplate from your mobile application development projects.
Visibility modifiers, factory patterns through companion objects, and proper inheritance delegation give you full control over how objects get created. These patterns show up constantly in production Kotlin code across Android, server-side Spring Boot applications, and cross-platform app development with KMP.
Get the constructor basics right and everything else in Kotlin’s type system clicks into place. Start with primary constructors, add complexity only when you actually need it.
- CSS Cheat Sheet - May 18, 2026
- How to Set Up VSCode for Python Development - May 16, 2026
- How Using One Platform Can Simplify Order Fulfillment - May 15, 2026



