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
https://kotlinlang.org/docs/delegated-properties.html#lazy-properties
lazy()
is a function that takes a lambda and returns an instance ofLazy<T>
, which can serve as a delegate for implementing a lazy property. The first call toget()
executes the lambda passed tolazy()
and remembers the result. Subsequent calls toget()
simply return the remembered result.
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.