0
0
Kotlinprogramming~15 mins

Primary constructor and init blocks in Kotlin - Deep Dive

Choose your learning style9 modes available
Overview - Primary constructor and init blocks
What is it?
In Kotlin, a primary constructor is a concise way to declare and initialize class properties directly in the class header. Init blocks are special blocks inside the class body that run immediately after the primary constructor to perform additional initialization. Together, they help set up an object when it is created, ensuring all properties have proper values.
Why it matters
Without primary constructors and init blocks, initializing objects would require more code and be less clear, making programs harder to read and maintain. They solve the problem of setting up objects cleanly and safely right when they are created, preventing errors from uninitialized properties and making code more concise and expressive.
Where it fits
Before learning this, you should understand basic Kotlin classes and properties. After mastering primary constructors and init blocks, you can learn about secondary constructors, property delegation, and advanced initialization patterns.
Mental Model
Core Idea
The primary constructor defines what data an object needs to start, and init blocks are like setup steps that run right after to prepare the object fully.
Think of it like...
Imagine building a sandwich: the primary constructor is choosing the bread and main fillings upfront, while the init blocks are the extra steps like spreading butter or adding seasoning before serving.
┌─────────────────────────────┐
│ Class Header                │
│ ┌─────────────────────────┐ │
│ │ Primary Constructor      │ │
│ │ (parameters & properties)│ │
│ └─────────────────────────┘ │
│                             │
│ ┌─────────────────────────┐ │
│ │ init Block(s)           │ │
│ │ (runs after constructor)│ │
│ └─────────────────────────┘ │
│                             │
│ Other class members         │
└─────────────────────────────┘
Build-Up - 7 Steps
1
FoundationDefining a Primary Constructor
🤔
Concept: Learn how to declare a primary constructor directly in the class header with parameters.
class Person(val name: String, val age: Int) // This class has a primary constructor with two parameters: name and age. // The 'val' keyword creates read-only properties automatically.
Result
You can create a Person object with name and age set immediately: val p = Person("Alice", 30) println(p.name) // Alice println(p.age) // 30
Understanding that primary constructors combine parameter declaration and property creation simplifies class design and reduces boilerplate.
2
FoundationUsing Init Blocks for Extra Setup
🤔
Concept: Init blocks run right after the primary constructor to execute additional initialization code.
class Person(val name: String, val age: Int) { init { println("Creating a person named $name who is $age years old") } } // The init block runs when an object is created.
Result
When you create a Person: val p = Person("Bob", 25) // Output: Creating a person named Bob who is 25 years old
Knowing init blocks run immediately after the constructor helps you add checks or side effects during object creation.
3
IntermediateMultiple Init Blocks Execution Order
🤔Before reading on: do you think multiple init blocks run in the order they appear or all at once? Commit to your answer.
Concept: Kotlin allows multiple init blocks, and they execute in the order they appear in the class body.
class Person(val name: String) { init { println("First init block: $name") } init { println("Second init block: $name") } } val p = Person("Carol") // Output: // First init block: Carol // Second init block: Carol
Result
Init blocks run top to bottom, allowing staged initialization steps.
Understanding the order of init blocks lets you organize complex setup logically and predictably.
4
IntermediateAccessing Constructor Parameters in Init Blocks
🤔Before reading on: can init blocks access primary constructor parameters directly, or only class properties? Commit to your answer.
Concept: Init blocks can use primary constructor parameters directly, even if they are not stored as properties.
class Person(name: String) { init { println("Hello, $name") } } val p = Person("Dave") // Output: Hello, Dave
Result
You can use constructor parameters in init blocks without declaring them as properties.
Knowing this allows temporary use of parameters for validation or setup without cluttering the class with unused properties.
5
IntermediateCombining Property Initialization and Init Logic
🤔Before reading on: do you think properties initialized in the primary constructor can be modified in init blocks? Commit to your answer.
Concept: Properties declared with 'val' cannot be reassigned, but 'var' properties can be modified inside init blocks.
class Person(var age: Int) { init { if (age < 0) { age = 0 // Correct invalid age } } } val p = Person(-5) println(p.age) // 0
Result
Init blocks can fix or adjust mutable properties after constructor runs.
Understanding mutability and init blocks helps enforce data correctness right when objects are created.
6
AdvancedInit Blocks vs Secondary Constructors
🤔Before reading on: do you think init blocks run when secondary constructors are used? Commit to your answer.
Concept: Init blocks always run after the primary constructor, even if a secondary constructor delegates to it.
class Person(val name: String) { constructor(name: String, age: Int) : this(name) { println("Secondary constructor with age: $age") } init { println("Init block for $name") } } val p = Person("Eve", 28) // Output: // Init block for Eve // Secondary constructor with age: 28
Result
Init blocks run before secondary constructor body executes.
Knowing this order prevents confusion about when initialization code runs in complex constructors.
7
ExpertHidden Initialization Order and Property Access
🤔Before reading on: can you safely use properties initialized in the primary constructor inside init blocks without risk? Commit to your answer.
Concept: Properties declared in the primary constructor are initialized before init blocks run, but properties declared in the class body initialize after init blocks, which can cause surprises.
class Person(val name: String) { val greeting = "Hello, $name" init { println(greeting) // Prints 'Hello, null' because greeting is not initialized yet } } val p = Person("Frank")
Result
Init blocks run before properties declared in the class body are initialized, which can lead to unexpected null or default values.
Understanding the exact initialization order helps avoid subtle bugs when mixing primary constructor properties and class body properties.
Under the Hood
When a Kotlin class is instantiated, the primary constructor parameters are passed first and properties declared in the constructor are initialized immediately. Then, all init blocks execute in the order they appear. After that, properties declared in the class body are initialized. This sequence ensures constructor parameters and init blocks can work together to set up the object before other properties finalize.
Why designed this way?
Kotlin's design aims for concise and safe initialization. Combining constructor parameters and init blocks allows flexible setup while keeping code readable. The separation between constructor properties and class body properties supports clear declaration and initialization order, avoiding uninitialized property errors common in other languages.
Object Creation Flow:

┌─────────────────────────────┐
│ Call Primary Constructor      │
│ (parameters passed in)       │
└──────────────┬──────────────┘
               │
               ▼
┌─────────────────────────────┐
│ Initialize Primary Constructor│
│ Properties (val/var)         │
└──────────────┬──────────────┘
               │
               ▼
┌─────────────────────────────┐
│ Execute Init Blocks in Order │
└──────────────┬──────────────┘
               │
               ▼
┌─────────────────────────────┐
│ Initialize Class Body        │
│ Properties (val/var)         │
└─────────────────────────────┘
Myth Busters - 4 Common Misconceptions
Quick: Do init blocks run before or after primary constructor properties initialize? Commit to your answer.
Common Belief:Init blocks run before any properties are initialized, so you cannot use properties inside them.
Tap to reveal reality
Reality:Properties declared in the primary constructor are initialized before init blocks run, so you can safely use them inside init blocks.
Why it matters:Believing otherwise causes unnecessary workarounds or confusion about property availability during initialization.
Quick: Does every constructor in Kotlin run init blocks? Commit to your answer.
Common Belief:Init blocks only run when the primary constructor is used directly, not with secondary constructors.
Tap to reveal reality
Reality:All constructors eventually call the primary constructor, so init blocks always run regardless of which constructor is used.
Why it matters:Misunderstanding this leads to bugs where initialization code is skipped unexpectedly.
Quick: Can you reassign 'val' properties inside init blocks? Commit to your answer.
Common Belief:'val' properties can be changed inside init blocks since they are just variables.
Tap to reveal reality
Reality:'val' properties are read-only and cannot be reassigned after initialization, even inside init blocks.
Why it matters:Trying to reassign 'val' properties causes compilation errors and confusion about immutability.
Quick: Are properties declared in the class body initialized before init blocks? Commit to your answer.
Common Belief:All properties are initialized before init blocks run, so init blocks can safely use any property.
Tap to reveal reality
Reality:Properties declared in the class body initialize after init blocks, so using them inside init blocks can lead to null or default values.
Why it matters:This misconception causes subtle bugs when init blocks rely on properties not yet initialized.
Expert Zone
1
Init blocks can be used to enforce complex validation logic that cannot be expressed in property declarations alone.
2
The order of property initialization and init blocks affects how inheritance and open properties behave during object creation.
3
Using init blocks with delegated properties can lead to unexpected timing issues if the delegate relies on fully initialized state.
When NOT to use
Avoid using init blocks for heavy logic or side effects; instead, use factory functions or secondary constructors for complex initialization. For immutable data classes, prefer property default values and require minimal init logic.
Production Patterns
In production Kotlin code, primary constructors with init blocks are used to enforce invariants and validate input early. Libraries often use init blocks to register listeners or initialize resources immediately after object creation.
Connections
Object-Oriented Constructors (General Programming)
Builds-on and refines the idea of constructors found in many languages.
Understanding Kotlin's primary constructor and init blocks clarifies how modern languages improve object initialization safety and conciseness compared to older constructor patterns.
Functional Programming Immutability
Contrasts with immutable data patterns by showing controlled mutability during initialization.
Knowing how init blocks allow temporary mutation during setup helps bridge understanding between mutable object construction and immutable functional data structures.
Manufacturing Assembly Line
Shares the pattern of staged setup steps to build a final product.
Seeing object initialization as an assembly line with ordered steps helps grasp why init blocks run in sequence and why order matters for correctness.
Common Pitfalls
#1Using class body properties inside init blocks expecting them to be initialized.
Wrong approach:class Person(val name: String) { val greeting = "Hello, $name" init { println(greeting) // May print 'Hello, null' or default } }
Correct approach:class Person(val name: String) { init { println("Hello, $name") // Safe to use constructor parameter } val greeting = "Hello, $name" }
Root cause:Misunderstanding the initialization order where class body properties initialize after init blocks.
#2Trying to reassign a 'val' property inside an init block.
Wrong approach:class Person(val age: Int) { init { age = 10 // Error: val cannot be reassigned } }
Correct approach:class Person(var age: Int) { init { age = 10 // Allowed because 'age' is var } }
Root cause:Confusing immutable 'val' properties with mutable 'var' properties.
#3Assuming init blocks do not run when using secondary constructors.
Wrong approach:class Person(val name: String) { constructor(name: String, age: Int) : this(name) { // init block won't run here } init { println("Init block") } }
Correct approach:class Person(val name: String) { constructor(name: String, age: Int) : this(name) { // init block runs before this } init { println("Init block") } }
Root cause:Not realizing all secondary constructors delegate to the primary constructor, triggering init blocks.
Key Takeaways
Primary constructors in Kotlin let you declare and initialize properties concisely right in the class header.
Init blocks run immediately after the primary constructor to perform extra setup or validation steps.
Multiple init blocks run in the order they appear, allowing staged initialization.
Properties declared in the primary constructor are initialized before init blocks, but class body properties initialize after, which affects their availability in init blocks.
Init blocks always run regardless of which constructor is used, ensuring consistent initialization.