不可为空的抽象变量能否在 KOTLIN 中抛出空指针异常?

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

免责声明: 这是用 Kotlin 编码的,它不是一些简单的初学者级别的代码。请不要标记它,因为你认为我是一些不知道空指针异常是什么的菜鸟。它比这复杂得多。我之前上传了这个问题,有人建议我查看空指针异常是什么,然后将我的问题标记为与空指针异常是什么的问题重复,然后立即关闭。如果您来自 Java,Kotlin 甚至不允许您在不分配对象的情况下创建对象(没有指定它们可以为 null 并在您每次使用它们时提供 null 安全检查 .我的问题不是那么简单,所以请先浏览一下。我还没来得及读完整篇文章,我的问题就被解决了,所以至少在你认为我只是愚蠢之前这样做。

信息:

这是一个 Swing 应用程序

我正在用 Kotlin 编写代码

我正在尝试为 Swing 的边界(搜索 "clean code boundary" 以获取更多信息)创建一个 classes 系统。本质上,我有两个 class 都继承自我制作的项目 class。

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)
        }
    }
}

另外两个 classes 除了重写组件变量什么都不做,然后 applyProperties() 中的 switch 语句可以使用组件变量类型来知道要设置哪些变量。就像现在一样,两个 classes 需要设置的变量是相同的,但我正在使用多态性,所以我可以稍后添加更多具有不同功能的 classes。这是其中之一,只是为了向您展示它们几乎不可能是原因。

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

这就是我 运行 抛出的空指针异常。

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()

调用 apply() 的 applyItems() 方法调用 add(),它接收一个 JComponent 作为输入,然后抛出异常。但是,我输入的组件总是在 add() 之前设置为 运行。它是一个抽象变量,总是在创建 class 项的实例时设置。此外,如果我忘记设置它,它应该连我都没有运行。我在 Kotlin 中对此进行编码,它具有极高的空安全性。除非我自己抛出一个空指针异常,否则我不应该能够得到一个空指针异常。它甚至不是一个可为空的变量,那么为什么 add() 抛出异常?

如果您想检查用于执行此操作的任何其他 classes,看看它们是否会导致空指针异常,我会将它们全部放在下面。

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)
}

这些是我用来简化创建菜单和菜单项的构建器模式,但使用键入 MenuItem(//parameters) 应该具有相同的效果(并产生相同的错误)。

possible ways to get NullPointerException in Kotlin 之一是:

  • 超级class构造函数调用一个开放成员,其在派生class中的实现使用未初始化状态

这正是您的示例中发生的情况。在 init 块中,它是基础 class Item 构造函数的一部分,您最终访问 component 属性,它尚未初始化,因为派生Menu class 构造函数调用了基础 class 构造函数并且尚未继续初始化其自身的属性。 https://kotlinlang.org/docs/inheritance.html#derived-class-initialization-order.

中详细描述了这种情况

如果在基础 class 构造过程中需要 component 值,您可以将它与其他参数一起传递给基础 class 构造函数。