ProgrammingKotlin middle developer

What is constructor delegation in Kotlin, how do primary/secondary constructors work, and what are the nuances of its usage?

Pass interviews with Hintsage AI assistant

Answer.

In Kotlin, every class can have one primary constructor and several secondary constructors. Constructor delegation is a mechanism where a secondary constructor must either directly call the primary constructor or another secondary constructor of the same class (which eventually leads to the primary). If a class inherits from another, every secondary constructor of the derived class must explicitly delegate the call to the superclass constructor if necessary.

Background

In Java, constructors can directly call each other using this() or super(), overloading constructors in various combinations. In Kotlin, this concept is formalized: a class can have only one primary constructor, and secondary constructors may use delegation logic that must be explicitly stated.

Problem

Improper implementation of delegation can lead to compilation errors: a primary constructor cannot be omitted if it is declared, and a super constructor must be called in a derived class if the base class does not have a default constructor. It is important to understand when init blocks are called, how parameters are passed, and how delegation affects the order of initialization.

Solution

An example of basic delegation:

class Person(val name: String) { constructor(name: String, age: Int) : this(name) { println("Secondary constructor: $name, $age") } }

If inheritance is used:

open class Parent(val name: String) class Child : Parent { constructor(name: String) : super(name) { println("Child secondary: $name") } }

Key Features:

  • A secondary constructor must explicitly delegate to another constructor of the same class (either primary or secondary).
  • Init blocks always execute after the primary constructor, regardless of how the secondary was called.
  • Calling super(...) is mandatory if the base class does not have a no-argument constructor.

Tricky Questions.

Can a secondary constructor not delegate to anything?

No, the compiler will require an explicit call to this(...) or super(...), otherwise, it will result in an error.

In what order do initializations and init blocks execute if a secondary constructor is used?

The primary constructor and init block(s) always execute first, followed by the initialization code of the secondary constructor.

class Demo(val value: String) { init { println("init block") } constructor(value: String, code: Int) : this(value) { println("secondary: $code") } } Demo("kotlin", 7) // output: // init block // secondary: 7

Can a descendant call only the primary constructor of the parent if its secondary constructor exists?

Yes, but only explicitly through super(...), secondary constructors are not directly visible to descendants.

Typical Mistakes and Anti-Patterns

  • Attempting to use initialization logic only in the secondary constructor and forgetting about init blocks.
  • The error "secondary constructor must delegate to primary constructor" when delegation is absent.
  • Inheritance with the inability to call the parent's constructor due to the absence of the required super constructor.

Real-Life Example

Negative Case

In a project, a secondary constructor does not call the primary, crucial initialization (e.g., setting required fields) does not take place, leading to bugs and crashes at runtime.

Pros:

  • Less code.

Cons:

  • Uninitialized properties.
  • Hard-to-detect errors.

Positive Case

All secondary constructors strictly delegate through this(...), necessary initialization is centralized in primary/init, and the structure is clear for maintenance.

Pros:

  • Guarantee that all objects are correctly and fully initialized.

Cons:

  • Requires a clear understanding of the order of initialization.