Stripped down to the bare essence, my problem is:
class MainActivity : AppCompatActivity() {
lateinit var testArray: Array<String>
lateinit var testMap: MutableMap<String, Array<Array<String>>>
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
testArray = arrayOf("0", "1", "2", "3", "4", "5", "6", "7", "8", "9")
testMap["a"] = arrayOf(
arrayOf("0", "1", "2", "3", "4", "5", "6", "7", "8", "9"),
arrayOf("a", "b", "c", "d", "e", "f", "g", "h", "i", "j"))
}
}
Why do I get this error for testMap...
lateinit property testMap has not been initialized
...but no error for testArray?
Edit for improved question quality:
I understand that lateinit variables must be initialised later, before they are used - but isn't that what I am doing? It seems to work with testArray
, but not with testMap
. Why are they different?
Edit 2: This variation...
testMap = mutableMapOf("a" to arrayOf(
arrayOf("0", "1", "2", "3", "4", "5", "6", "7", "8", "9"),
arrayOf("a", "b", "c", "d", "e", "f", "g", "h", "i", "j")))
...works.
I don't know if you got what's happening here, but just in case
Usually when you create a field (a top-level variable, not inside a function or anything), you have to initialise it with a value:
// needs to be initialised to something!
val coolString1: String
// now we're going places
val coolString2: String = "ok here"
But sometimes you want to define that variable, but you don't actually have the thing you want to assign to it yet (very common in Android, when the actual setup for Activities and Fragments happens in lifecycle callbacks, long after the object was constructed). You want to initialise it, later. That's what lateinit
is for
// has to be a var - and look, you're not assigning a value, and it's fine!
lateinit var coolString: String
fun calledLater() {
coolString = "sup"
}
So lateinit
is you promising to initialise that variable before anything tries to access it. The compiler is trusting you to take care of it.
But did you?
// no array assigned! Will init later
lateinit var testArray: Array<String>
// no map assigned! Will init later
lateinit var testMap: MutableMap<String, Array<Array<String>>>
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
// assigning that array you promised to! perfect
testArray = arrayOf("0", "1", "2", "3", "4", "5", "6", "7", "8", "9")
// trying to add something to a map that hasn't been created yet!
// you're accessing it before it's been assigned, you broke your promise
testMap["a"] = arrayOf(
arrayOf("0", "1", "2", "3", "4", "5", "6", "7", "8", "9"),
arrayOf("a", "b", "c", "d", "e", "f", "g", "h", "i", "j"))
}
So you haven't actually assigned a map to that variable yet - there's nothing there to add values to. Normally you'd get warned/prevented from doing this kind of thing, but lateinit
allows you to take control, tell the compiler "don't worry, I got this" and safely initialise something later without having to make it nullable. But you have the responsibility to make sure it will always be initialised before it's accessed
It's an easy enough fix here - just make a map, you can use mapOf
the same way you're using arrayOf
if you like, mapOf("a" to arrayOf(...
But it's good to know what you need to be aware of in general. lateinit
can be really useful, but you need to know what you're doing!