article banner

Exercise: Lateinit delegate

Implement a Lateinit delegate that makes a property behave like a lateinit property so that the delegate can keep set values, but it should not require an initial value. If the getter is called before the property is set, it should throw an IllegalStateException exception with the message "Uninitialized lateinit property {name}".

val a by Lateinit<Int>() a = 1 println(a) // 1 val b by Lateinit<String>() b = "ABC" println(b) // ABC val c by Lateinit<String>() println(c) // IllegalStateException: // Uninitialized lateinit property c

This delegate should support nullable types.

val a by Lateinit<Int?>() a = 1 println(a) // 1 a = null println(a) // null

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/Lateinit.kt. You can find there unit tests.

Hint: You can make your class implement the ReadWriteProperty<Any?, T> interface to make it a property delegate.

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

Playground

import org.junit.Test import kotlin.properties.Delegates import kotlin.properties.ReadWriteProperty import kotlin.reflect.KProperty import kotlin.test.assertEquals import kotlin.test.assertIs // TODO // Implement Lateinit delegate here // Easy: Uncomment the first 2 tests and implement Lateinit so it works for Int type // Medium: Uncomment the first 3 tests and implement Lateinit so it works for all non-nullable types // Hard: Uncomment all tests and implement Lateinit so it works for all types class LateinitTest { @Test fun `Throws exception if accessed before initialization`() { var value: Int by Lateinit() val res = runCatching { println(value) } val exception = assertIs<IllegalStateException>(res.exceptionOrNull()) assertEquals("Uninitialized lateinit property value", exception.message) var value2: Int by Lateinit() val res2 = runCatching { println(value2) } val exception2 = assertIs<IllegalStateException>(res2.exceptionOrNull()) assertEquals("Uninitialized lateinit property value2", exception2.message) } @Test fun `Behaves like a normal variable for Int`() { var value: Int by Lateinit() value = 10 assertEquals(10, value) value = 20 assertEquals(20, value) } @Test fun `Behaves like a normal variable for String`() { var value: String by Lateinit() value = "AAA" assertEquals("AAA", value) value = "BBB" assertEquals("BBB", value) } @Test fun `Behaves like a normal variable for nullable String`() { var value: String? by Lateinit() value = "AAA" assertEquals("AAA", value) value = null assertEquals(null, value) value = "BBB" assertEquals("BBB", value) } }