article banner

Exercise: Mutable lazy delegate

The lazy delegate can only be used to val variables. Implement a MutableLazy delegate that can be used to var variables. It should behave like a lazy delegate but supporting read-write properties. If the property getter is called before the setter, the MutableLazy delegate should initialize the property using its lambda expression. If the property getter is called after the setter, it should return the value that was set.

fun calculate(): Int { print("Calculating... ") return 42 } var a by mutableLazy { calculate() } println(a) // Calculating... 42 println(a) // 42 a = 1 println(a) // 1 var b by mutableLazy { calculate() } b = 2 println(b) // 2
fun <T> mutableLazy( initializer: () -> T ): ReadWriteProperty<Any?, T> = TODO()

This problem can either be solved in the below playground or you can clone kotlin-exercises project and solve it locally. In the project, you can find code template for this exercise in advanced/delegates/MutableLazy.kt. You can find there starting code and unit tests.

Once you are done with the exercise, you can check your solution here.

Playground

import org.junit.Assert import org.junit.Test import kotlin.properties.ReadWriteProperty import kotlin.reflect.KProperty import kotlin.system.measureTimeMillis fun <T> mutableLazy( initializer: () -> T ): ReadWriteProperty<Any?, T> = TODO() class MutableLazyTest { @Test fun `Do not initialize if initialized`() { val time = measureTimeMillis { var game: Game? by mutableLazy { readGameFromSave() } game = Game() print(game) } assert(time in 0..100) } @Test fun `Initializes if not initialized`() { val time = measureTimeMillis { val game: Game? by mutableLazy { readGameFromSave() } print(game) } assert(time in 450..550) } @Test fun `Do not initialize again if already initialized`() { val time = measureTimeMillis { val game: Game? by mutableLazy { readGameFromSave() } print(game) print(game) print(game) } assert(time in 450..550) } @Test fun `MutableLazy should accept nullable values`() { val lazy by mutableLazy<String?> { null } Assert.assertNull(lazy) var lazy2 by mutableLazy<String?> { "A" } lazy2 = null Assert.assertNull(lazy2) } private class Game() private fun readGameFromSave(): Game? { Thread.sleep(500) return Game() } }