Understand Kotlin Multiple Constructors

Simple examples to showcase primary and secondary / multiple constructors in Kotlin

ยท

3 min read

Understand Kotlin Multiple Constructors

In Kotlin, you have one primary and many secondary constructors.

Primary Constructor

This is primary constructor with one parameter.

class Example constructor(private val param1: String) {
    init {
        println("init is called.")
    }
}

You can also omit the constructor keyword.

class Example(private val param1: String) {
    init {
        println("init is called.")
    }
}

You can NOT initialize in primary constructor. Instead, you need to initialize your code in init{} block.

Secondary Constructor

There are 2 secondary constructors below.

class Example(private val param1: String) {

    init {
        println("init is called.")
    }

    //First secondary constructor
    constructor(
        param1: String, 
        param2: String) : this(param1) {

        println("Second constructor is called")
    }

    //Second secondary constructor
    constructor(
        param1: String, 
        param2: String, 
        param3: String) : this(param1) {

        println("Third constructor is called")
    }
}

Few important notes here:

  • You must call the primary constructor (e.g. calling this(param1)). Not really! See next section - Call Another Secondary Constructor
  • You can declare var or val in the secondary constructor parameter
  • You can initialize your code in secondary constructor

Please note that primary constructor together with the init{} block is called first before the secondary constructor initialization.

So, if I call the third constructor,

val obj = Example(param1="1", param2="2", param3="3")

the output will be like this.

init is called.
Third constructor is called

Call Another Secondary Constructor

Instead of calling the primary constructor in your secondary constructor, you can also call another secondary constructor.

In this example, the second secondary constructor calls the first secondary constructor.

class Example(private val param1: String) {

    init {
        println("init is called.")
    }

    //First secondary constructor
    constructor(
        param1: String,
        param2: String) : this(param1) {

        println("Second constructor is called")
    }

    //Second secondary constructor
    constructor(
        param1: String,
        param2: String,
        param3: String) : this(param1, param2) {

        println("Third constructor is called")
    }
}

If I call the third constructor, the output looks like this:

init is called.
Second constructor is called
Third constructor is called

Empty Primary Constructor

This is an empty primary constructor and secondary constructor example.

class Example() {
    init {
        println("init is called.")
    }

    constructor(param1: String): this() {
        println("Second constructor is called")
    }
}

However, you don't really need to call this() in your secondary constructor. You also need to change Example() to Example.

class Example {
    init {
        println("init is called.")
    }

    constructor(param1: String) {
        println("Second constructor is called")
    }
}

Secondary Constructor Use case

I encountered the needs of using secondary constructor when I want to inject the Hilt dependency into my View Model.

I have code like this where preview parameter is used for @preview jetpack compose. It is set to true only in @preview. However, if I port this change to use Hilt dependency injection, it fails to inject this dependency.

class MainViewModel(
    private val repository: ArticlesRepository,
    preview: Boolean = false,
) : ViewModel() {
   /*...*/
}

Thus, I break this to secondary constructor below.

class MainViewModel(
    private val repository: ArticlesRepository) : ViewModel() {
    constructor (
        repository: ArticlesRepository, 
        preview: Boolean) : this(repository) {
        /*...*/
    }
}

So I use @Inject constructor for the primary constructor and the secondary constructor is used for @preview.

With Hilt implementation, it looks like this

@HiltViewModel
class MainViewModel
    @Inject constructor(
        private val repository: ArticlesRepository,
    ) : ViewModel() {

    constructor (
        repository: ArticlesRepository, 
        preview: Boolean) : this(repository) {
        /*...*/
    }
    /*...*/
}

Conclusion

I haven't used a lot of multiple constructors, have you? So, I documented this here for my future reference.

Did you find this article valuable?

Support Vincent Tsen by becoming a sponsor. Any amount is appreciated!

ย