How to know if a not null variable by Delegate (Delegates.notNull()) has been initialized


#1

I’m having a class member variable that cannot be null but cannot be initialised upon instance creation. Therefore I’m using Delegates.notNull().

However, is there a way to know if the variable has been set without triggering the IllegalStateException thrown by the notNull Delegate?

Why I’m doing this? Because I’m having “client code” that’s responsible for setting the variable to something (before it’s ready by any other code), but I don’t want readers of the variable to worry about the null state of the variable (that will never happen).

Thanks!


#2

Delegates.notNull does not, but it isn’t rocket science to write your own delegates, you only have to implement two methods.

Here is a class combining the NotNull with Observable:

class ObservableNotNullVar<T: Any>(val observer: KProperty<*>.(T) -> Unit) : ReadWriteProperty<Any?, T> {
    private var value: T? = null

    public override fun getValue(thisRef: Any?, property: KProperty<*>): T {
        return value ?: throw IllegalStateException("Property ${property.name} should be initialized before get.")
    }

    public override fun setValue(thisRef: Any?, property: KProperty<*>, value: T) {
        this.value = value

        property.observer(value)
    }
}

val x by ObservableNotNullVar<String> {
    println("property $name was set to $it")
}

So let’s say you had a class that you wanted to check if all properties were initalized you could do something like this:

class MyClass
{
    val unsetProperties = MyClass::class.declaredMemberProperties.toMutableSet()

    val foo by ObservableNotNullVar<String> { initialized() }
    val bar by ObservableNotNullVar<Int> { initialized() }

    private fun KProperty<*>.initialized() { unsetProperties.remove(this) }
}

#3

Hi and thanks for the answer Dale King!

You’re answer led my on the right track I think. However my situation was more complex as described as my observer was a function in an interface and variables in an interface cannot use “by [Delegate]”. My solution was instead to let the observing interface require a boolean property: initialized and then let every implementation use ObservableNotNullVar and set the initialized property upon initialization.

After implementing this I came across lateinit - which I hoped would save me, but I did not figure out any way to read the state of a lateinit var either :frowning: