swingkotlinnullpointerexceptionjcomponent

Can abstract variables that aren't nullable throw Null Pointer Exceptions in KOTLIN?


DISCLAIMER: This is coded in Kotlin and it's not some simple beginners level code. Please don't flag it because you think I'm some noob who doesn't know what a Null Pointer Exception is. It's quite a bit more complicated than that. I uploaded this question previously and was suggested to look up what Null Pointer Exceptions were and then had my question marked as a duplicate to a question about what Null Pointer Exceptions were and then promptly closed. If you are coming from Java, Kotlin doesn't even let you create objects without assigning them (without specifying that they are nullable and providing a null safety check every time you use them). My question's not that simple, so please look through it first. My question was closed before I could even finish reading the whole thing, so at least do that before you assume I'm just stupid.

Information:

This is a Swing app

I'm coding it in Kotlin

I am trying to create a system of classes for a boundary (search "clean code boundary" for more info) to Swing. Essentially, I have two classes that both inherit from the Item class I made.

abstract class Item(var text: String = "", var icon: Icon? = null, var items: ArrayList<Item> = ArrayList()) {

    abstract var component: JComponent

    init {
       applyProperties()
        applyItems()
    }

    fun applyProperties() {
        when (component) {
            is JMenu -> {
                (component as JMenu).text = text
                (component as JMenu).icon = icon
            }
            is JMenuItem -> {
                (component as JMenuItem).text = text
                (component as JMenuItem).icon = icon
            }
        }
    }
    fun applyItems() {
        for (item in items) {
            apply(item)
        }
    }
    private fun apply(item: Item) {
        component.add(item.component)
    }
    companion object {
        fun ArrayList<Item>.addItems(vararg items: Item) {
            this.addAll(items)
        }
    }
}

The two other classes do nothing but override the component variable, and then the switch statement in applyProperties() can use the component variables type to know what variables to set. As it is now, the variables that need to be set are the same for both classes, but I'm using polymorphism so I can add more classes later with different functions. Here is one of them, just to show you that there is almost no way they could be the cause.

class Menu(text: String = "", icon: Icon? = null, items: ArrayList<Item> = ArrayList()): Item(text, icon, items) {
   override var component: JComponent = JMenu()
}

This is what I ran that threw a Null Pointer Exception.

var menu: JMenu = MenuBuilder().set { menu ->
    menu.text = "Save"
    menu.items.addItems(

        MenuItemBuilder().set { menuItem ->
            menuItem.text = "Save"
        }.build(),

        MenuItemBuilder().set { menuItem ->
            menuItem.text = "Save As"
        }.build()
    )
}.build()

The applyItems() method called on apply() which called on add(), which receives one JComponent as input and then threw the exception. The component that I have entered, however, is always set before add() is run. It's an abstract variable that is always set on the creation of an instance of the class Item. Additionally, if I forgot to set it, it should have not even let me run. I'm coding this in Kotlin which has extreme null safety. I shouldn't be able to get a Null Pointer Exception unless I throw one myself. It's not even a nullable variable, so why is add() throwing an exception?

If you want to inspect any other classes used to do this, to see if they could be causing the null pointer exception, I will put them all below.

class MenuBuilder {
    var text: String = ""
    var icon: Icon? = null
    var items: ArrayList<Item> = ArrayList()

    fun set(builderFunction: (MenuBuilder) -> Unit): MenuBuilder {
        builderFunction(this)
        return this
    }

    fun build() = Menu(text, icon, items)
}

class MenuItemBuilder {
    var text: String = ""
    var icon: Icon? = null
    var items: ArrayList<Item> = ArrayList()

    fun set(builderFunction: (MenuItemBuilder) -> Unit): MenuItemBuilder {
        builderFunction(this)
        return this
    }

    fun build() = MenuItem(text, icon, items)
}

These are the builder patterns I used to make the creation of my Menus and MenuItems easier but using typing MenuItem(//parameters) should have the same effect (and produce the same error).


Solution

  • One of possible ways to get NullPointerException in Kotlin is:

    That's exactly what is happening in your example. In the init block, which is a part of base class Item constructor, you eventually access component property, which has not been yet initialized because the derived Menu class constructor has called the base class constructor and has not been proceeded to initializing its own properties. The situation is described in detail in https://kotlinlang.org/docs/inheritance.html#derived-class-initialization-order.

    If component value is required during base class construction, you can pass it along with the other parameters to the base class constructor.