Andrew
Andrew

Reputation: 4712

How to execute code in init block after property y is initialized

I would like to call some code inside my class init block, after a property y is initialized. I've tried it with this::y.isInitialized ..., but the problem here is that at the creation of the class, the property is not initialized (which it gets during runtime).

ExampleClass

class Example {
     lateinit var y: String // this gets initialized after the creation of the fragment at a later time

     init {
         if(this::y.isInitialized) {
            doStuff()
         }
     }

}

Szenario

val testObject = Example()
testObject.y = "START INIT CODE" // here it should execute the init block

Is this even possible? Or should I change the init block with a function call after I ensure that y is initialized?

Upvotes: 0

Views: 2013

Answers (3)

Konstantin Raspopov
Konstantin Raspopov

Reputation: 1615

You can use an observable property delegate for this purpose. Here's an example:

import kotlin.properties.Delegates

class Example {
    var y: Int? by Delegates.observable(initialValue = null) { _, oldValue, _ ->
        if (oldValue == null) {
            doStuff()
        }
    }

    private fun doStuff() {}
}

If you need more sophisticated logic, like to allow assigning property only once, you may want to implement your own property delegate similar to the proposed.

Upvotes: 0

Sinner of the System
Sinner of the System

Reputation: 2966

you can do something like this:

class Example {

    var y: String = "UNINITIALIZED"
        set(value) {
            if (field == "UNINITIALIZED") doStuff(value)
            field = value
        }

    fun doStuff(y: String) {...}
}

or if you need to modify the initial value:

class Example {

    var y: String = "UNINITIALIZED"
        set(value) {
            field = if (field == "UNINITIALIZED") doStuff(value) else value
        }

    fun doStuff(y: String): String {...}

}

Upvotes: 0

Tenfour04
Tenfour04

Reputation: 93601

The init block is exclusively for code called at instantiation time, so it is not applicable to something that is lateinit.

I also have to mention, a public lateinit var in my opinion is a huge code smell. There's no way to force outside classes to treat it appropriately at compile time. lateinit is intended for properties that are only usable (visible) after some post-construction event.

Assuming we don't care about the design issue, one way you could solve it is with a second nullable backing property.

class Example {
    private var _y: String? = null
        set(value) {
            val needInitialization = field == null
            field = value
            if (needInitialization) {
                // do initialization work here
            }
        }
    
    var y: String
        get() = _y ?: throw UninitializedPropertyAccessException("y must be initialized before getting.")
        set(value) {
            _y = value
        }
}

Upvotes: 1

Related Questions