Late initialization

2 minute read

Sometimes, we want to initialize the properties of the class after it is created, but in a separate member function instead of using lazy

Let’s take a look at below code. Box interface has a function setUp that initializes instances. Suppose we want to reuse a library that creates and manipulates Box. This library requires subclass initialization in setUp() instead of in a constructor.

interface Box {
  fun setUp()
}

class ToyBox: Box {
  private var items: String? = null
  override fun setUp() {
    items = "cars, trains, lego"
  }
  fun checkToy(toy: String): Boolean =
    items?.contains(toy) ?: false
}

val toyBox = ToyBox()
toyBox.setUp()
toyBox.checkToy("lego")

As you notice, in ToyBox class, we could not just define items as String because that would requires us to provide the initial value which is not null. We may be tempted to initialize with an empty String, but it is a bad practice because we will not know if empty is the real value of items. We use null to indicate that it has not yet initialized.

If we make items as optional (String?), it means we must check for null in all member functions, eg. in checkToy(), we have to make sure that items is not null before we could do contains() function call. However, we know in our mind that we will call setUp() function to initialize, so null check is unnecessary in reality.

The lateinit property fixes this problem. Let’s take a look at the same code with lateinit.

interface Box {
  fun setUp()
}

class ToyBox: Box {
  lateinit var items: String? = null
  override fun setUp() {
    items = "cars, trains, lego"
  }
  fun checkToy(toy: String): Boolean =
    toy in items
}

val toyBox = ToyBox()
toyBox.setUp()
toyBox.checkToy("lego")

Now, the checkToy() does not need to worry if items is going to be null anymore. It is safely defined as non-nullable property.

Limitation

lateinit could be used on a property inside the body of a class, a top-level property, or local var.

However,

  • lateinit could only be used on var property, not a val.
  • The property must be non-nullable type
  • The property could not be a primitive type
  • lateinit could not be used used in abstract class or interface
  • lateinit is not allowed for custom get() or set()
  • The IDE would not be able to detect if we forget to initialize or there will be no compile-time error

Conclusion

The lateinit is a better way of expression in Kotlin if we are sure that the property will be initialized later by a function, and not by constructor. It converts the property into non-nullable, which has both benefits and pitfalls. Since we would not be able to detect it during compile time, the only time that we could find out is in runtime. ::items.isInitialized might give some helps, we should take caution about it.

Tags:

Categories:

Updated: