I am receiving a NullPointerException which I believe is due to the way objects are initialised but cannot find any supporting documentation.
I have this example code which illustrates the problem in Scala 2.12.7, I have found repeatable results in Scala 3.1.3 also:
abstract class Item(val collectionName: String)
abstract class ItemCollection(val name: String)
object TechItems extends ItemCollection("tech") {
// referencing 'name' from 'ItemCollection' superclass
case object TV extends Item(collectionName = name)
val items: Map[String, Item] = Map("tv" -> TV)
}
object Test1 extends App {
// prints 'tech'
println(TechItems.items.get("tv").map(_.collectionName))
}
object Test2 extends App {
// prints 'tech'
println(TechItems.TV.collectionName)
// throws NullPointerException
println(TechItems.items.get("tv").map(_.collectionName))
}
When running Test1
, the code behaves as you'd expect. When running Test2
, we now receive a NullPointerException
when accessing the map after accessing the TV
object directly.
When I no longer reference a field from the superclass, the issue no longer occurs:
...
object TechItems extends ItemCollection("tech") {
// using String instead of reference to superclass field
case object TV extends Item(collectionName = "mycollection")
val items: Map[String, Item] = Map("tv" -> TV)
}
...
object Test2 extends App {
// prints 'mycollection'
println(TechItems.TV.collectionName)
// prints 'Some(mycollection)'
println(TechItems.items.get("tv").map(_.collectionName))
}
My current understanding of how TechItems
is initialised:
- We access
TechItems.TV.collectionName
which begins initialisingTechItems
- An
ItemCollection("tech")
is created whose fields are then available inside ofTechItems
(depending on access modifiers of said superclass fields) TV
is initialised and references the superclass fieldname
items
is initialised and referencesTV
as a value for key"tv"
I am sure that understanding is wrong but that is what I am here to learn.
My current theory for the NullPointerException:
- We access
TechItems.TV.collectionName
which begins initialisingTechItems
items
is initialised alongsideTV
, butitems
captures an uninitialisedTV
asnull
- Our access to
TechItems.TV.collectionName
returns the value of"tech"
TechItems.items.get("tv")
returnsSome(null)
becauseTV
at the point of initialisingitems
wasnull
, due to not being initialised.NullPointerException
is thrown
To me it feels like a somewhat farfetched theory. I am sure my lack of understanding is shown here and there is an explanation in some documentation that I have failed to find. Why do I get this NullPointerException
? What is the initialisation order? And why does removing the reference to a superclass field affect this initialisation?