DEV Community

Cover image for Understand Kotlin Multiple Constructors
Vincent Tsen
Vincent Tsen

Posted on • Edited on • Originally published at vtsen.hashnode.dev

Understand Kotlin Multiple Constructors

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

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.")
    }
}
Enter fullscreen mode Exit fullscreen mode

You can also omit the constructor keyword.

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

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")
    }
}
Enter fullscreen mode Exit fullscreen mode

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")
Enter fullscreen mode Exit fullscreen mode

the output will be like this.

init is called.
Third constructor is called
Enter fullscreen mode Exit fullscreen mode

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")
    }
}
Enter fullscreen mode Exit fullscreen mode

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

init is called.
Second constructor is called
Third constructor is called
Enter fullscreen mode Exit fullscreen mode

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")
    }
}
Enter fullscreen mode Exit fullscreen mode

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")
    }
}
Enter fullscreen mode Exit fullscreen mode

Secondary Constructor Use case

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

%[https://vtsen.hashnode.dev/convert-view-model-to-use-hilt-dependency-injection]

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() {
   /*...*/
}
Enter fullscreen mode Exit fullscreen mode

Thus, I break this to secondary constructor below.

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

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) {
        /*...*/
    }
    /*...*/
}
Enter fullscreen mode Exit fullscreen mode

Conclusion

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


Originally published at https://vtsen.hashnode.dev.

Top comments (0)