0

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?

Andrew
  • 4,264
  • 1
  • 21
  • 65
  • you could use [observer pattern](https://kotlin-code.com/design-patterns/behavioral-patterns/observer-pattern/). In the comment yuo mention fragment - are you writing for Android? if yes you could use [LiveData](https://developer.android.com/topic/libraries/architecture/livedata) – Stachu Jan 11 '21 at 18:34
  • Yes in this example I am writing for android. But moreover, I am interested in a general solution – Andrew Jan 11 '21 at 18:44

3 Answers3

1

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
        }
}
Tenfour04
  • 83,111
  • 11
  • 94
  • 154
  • I don't think that it is a design issue in my case. My ExampleClass is only used inside on other class and I know that I have to assign the lateinit property inside this other class. – Andrew Jan 11 '21 at 18:47
0

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 {...}

}
Sinner of the System
  • 2,558
  • 1
  • 8
  • 17
0

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.

Konstantin Raspopov
  • 1,565
  • 1
  • 14
  • 20