Picture this: you've been staring at a stack trace for twenty minutes, wondering why your Android app crashes the moment it tries to create an object. The culprit? A mismatch in how you declared your constructor. It happens to every developer at some point, and once you understand Kotlin's constructor system, you'll spot the problem instantly.
Constructor logic trips up even experienced programmers moving from Java to Kotlin. The syntax looks familiar, but nuances can catch you off guard. This guide breaks down everything you need to know about Kotlin constructors and how they apply to Android development.
What happened
- Constructors initialize objects when they're created
- Kotlin distinguishes between primary and secondary constructors
- Primary constructors live in the class header; secondary constructors use the "constructor" keyword
- Secondary constructors must delegate to the primary constructor using "this()"
- Initialization blocks ("init") run when primary constructors need complex setup logic
Kotlin Constructor Basics
In Kotlin, a constructor serves one primary purpose: getting an object ready for use the moment it springs to life. Think of it as the setup routine that runs before anything else can happen with that object.
Unlike languages that bundle everything into a single approach, Kotlin gives you two distinct options. The primary constructor lives right in the class declaration, keeping your code clean. The secondary constructor lives inside the class body, offering flexibility for complex scenarios.
Primary Constructor Deep Dive
The primary constructor belongs in the class header. When your initialization logic is straightforward, this approach keeps your class definition concise.
Here's where it gets interesting: if you need more than simple assignment, you can add an "init" block. This block runs every time the class is instantiated, executing your setup code in the order it appears.
“`kotlin
Class Greeter(name: String, id: Int) {
Val displayName: String
Val displayId: Int
Init {
DisplayName = name.capitalize()
DisplayId = id
Println("Greeter created with ID $displayId")
}
}
“`
The parameters "name" and "id" passed to the primary constructor don't automatically become properties—they lack "val" or "var" keywords. Inside the init block, you handle them however you need.
Secondary Constructors and Delegation
Secondary constructors give you flexibility when the primary constructor alone doesn't handle your needs. You can define multiple secondary constructors in a single class, each with different parameter lists.
The critical rule: every secondary constructor must delegate to the primary constructor. Kotlin enforces this using the "this()" keyword placed after the constructor declaration. This delegation happens before your secondary constructor code runs.
When you need one secondary constructor to call another, use the same "this()" pattern. The called constructor executes first, then control passes back.
Inheritance and Constructors
Android development frequently involves inheritance, and Kotlin handles parent-child constructor relationships with the "super" keyword. When a child class needs to call a parent constructor, "super" does the work.
This matters because Android framework classes have their own constructor requirements. When you extend Activity, Service, or any other component, you're working within this inheritance chain.
Our Analysis: Why This Matters for Android Devs
Understanding constructor delegation isn't just academic. When you build Android apps, you're constantly creating custom versions of framework classes. Every time you extend View, Activity, or Fragment, you're dealing with constructors behind the scenes.
The delegation requirement exists for good reason. It ensures parent class initialization always happens before child class code runs. In Java, skipping this step accidentally gets you a runtime crash. Kotlin's syntax makes the dependency explicit—no guessing about initialization order.
The trade-off:** Developers accustomed to Java often struggle with restructuring initialization logic. Java lets you overload constructors freely. Kotlin's delegation requirement means you sometimes need to rethink your approach rather than just porting code directly.
Who wins:** Teams that invest time understanding the system early. Once the mental model clicks, you'll write more predictable initialization code and debug issues faster.
What to watch:** When converting Java projects, pay attention to constructor overloads. You'll need to decide whether to use multiple secondary constructors or push logic into init blocks. Both work, but the latter often produces cleaner code.
When to Use Each Constructor Type
| Scenario | Recommended Approach |
|---|---|
| Simple property initialization | Primary constructor with val/var |
| Need validation or transformation | Primary constructor + init block |
| Multiple initialization patterns | Primary + secondary constructors |
| Extending Android framework classes | Primary constructor with super() call |
| Providing defaults or overloads | Secondary constructors with this() delegation |
Bottom Line
Kotlin's constructor system rewards understanding. Once you grasp how primary and secondary constructors work together—and why delegation matters—you'll write cleaner initialization code and debug issues faster. The delegation requirement isn't a limitation; it's a safeguard that makes your Android apps more predictable.
The next time you create a custom View or extend a framework class, pay attention to what's happening in the constructor chain. That knowledge pays dividends across every Android project you touch.
Frequently asked questions
What's the difference between primary and secondary constructors in Kotlin?
The primary constructor lives in the class header and is the preferred initialization method. Secondary constructors sit inside the class body and must delegate to the primary constructor using the "this()" keyword.
Can a Kotlin class have both primary and secondary constructors?
Yes, but every secondary constructor must call the primary constructor using "this()". This ensures parent initialization always runs before child code.
What is an init block in Kotlin?
An init block contains code that runs during object initialization. It executes when the primary constructor is called, and it's useful when you need more than simple property assignment.
How do constructors work with inheritance in Kotlin?
Child classes call parent constructors using the "super" keyword. For primary constructors, this happens in the class declaration. For secondary constructors, you add ": super(parameters)" after the constructor signature.
Why does Kotlin require delegation between constructors?
Delegation ensures parent class initialization completes before child class code runs. This prevents runtime errors that would occur if a subclass tried to use parent properties before they were set up.