Initializing map in companion object throws ExceptionInInitializerError

Viewed 139

Consider the following class:

sealed class Fruit(val id: String, val label: String) {
  object orange : Fruit("Citrus sinensis", "orange")
  object watermelon : Fruit("Citrullus lanatus", "watermelon")
  
  companion object {
    val values = listOf(orange, watermelon)
    val valuesByID = values.associateBy { it.id } // <<< error here
  }
}

This class compiles fine, but when it is referenced at runtime (eg: val oj = Fruit.orange) an error will be thrown.

java.lang.ExceptionInInitializerError
        at com.example.sandbox.App.<clinit>(App.kt:13)
        at java.lang.reflect.Constructor.newInstance(Native Method)
        at java.lang.Class.newInstance(Class.java:1606)

However, if the valuesByID line is removed, then the error will not be thrown. Why is this happening?

The runtime environment is Android, if that matters.

2 Answers

To add onto Sergei's answer, you can workaround the issue by lazily initializing the companion object's values.

sealed class Fruit(val id: String, val label: String) {
  object orange : Fruit("Citrus sinensis", "orange")
  object watermelon : Fruit("Citrullus lanatus", "watermelon")
  
  companion object {
    val values by lazy { listOf(orange, watermelon) } // Lazy init
    val valuesByID by lazy { values.associateBy { it.id } } // <<< Lazy init, no error here
  }
}

And yes, you do need to wrap every reference in a lazy block, not just the line that emits the error.

Related