Delegate value initialisation with “by lazy” in Kotlin

Using unmodifiable variables – so called values in Kotlin with keyword val – should be the default where possible but it’s not always practical to initialise all values within the constructor. Kotlin offers the option to use “by lazy”.

With some common kinds of properties, even though you can implement them manually every time you need them, it is more helpful to implement them once, add them to a library, and reuse them later.

– Lazy properties: the value is computed only on first access.

– Observable properties: listeners are notified about changes to this property.

– Storing properties in a map instead of a separate field for each property.

https://kotlinlang.org/docs/delegated-properties.html

lazy() is a function that takes a lambda and returns an instance of Lazy<T>, which can serve as a delegate for implementing a lazy property. The first call to get() executes the lambda passed to lazy() and remembers the result. Subsequent calls to get() simply return the remembered result.

https://kotlinlang.org/docs/delegated-properties.html#lazy-properties

The following test gives you an easy sample that allows you to use overridable member methods to calculate values on initialisation:

import org.assertj.core.api.Assertions.assertThat
import org.junit.jupiter.api.Test

class ByLazyTest {

    @Test
    fun test() {
        val instanceA = A("a")
        val instanceB = B("b")

        assertThat(instanceA.testStr).isEqualTo("testStrGenA")
        assertThat(instanceA.value1).isEqualTo("a")
        assertThat(instanceB.testStr).isEqualTo("testStrGenB")
        assertThat(instanceB.value1).isEqualTo("b")
    }

    open class A constructor(
        val value1: String
    ) {
        val testStr: String by lazy { testStrGen() }

        open fun testStrGen(): String {
            return "testStrGenA"
        }
    }

    open class B constructor(
        value1: String
    ) : A(value1) {

        override fun testStrGen(): String {
            return "testStrGenB"
        }

    }
}

This initialisation is by default synchronised.