繁体   English   中英

为什么这个 Kotlin/Android 代码会抛出 java.lang.ArrayIndexOutOfBoundsException?

[英]Why this Kotlin/Android code throws java.lang.ArrayIndexOutOfBoundsException?

    var i = 0

    arrayOf<CheckBox>(binding.chkBackup, binding.chkBackupEnc, binding.chkDrive,
            binding.chkDriveEnc, binding.chkMp3).forEach {

        it.setOnClickListener { chk ->
            root.model.checkedBackups.value?.set(i++, (chk as? CheckBox)?.isChecked ?: false)
        }

    }

第 267 行 id 带有 'set' function 调用的那个

java.lang.ArrayIndexOutOfBoundsException: length=5; index=5
        at info.gryb.gac.mobile.fragments.StoreFragment$onViewCreated$$inlined$forEach$lambda$1.onClick(StoreFragment.kt:267)

下面有一条关于在单击事件期间评估“i”的评论,这实际上是它的工作原理,但是,它与许多语言中的一个常见概念相矛盾,即原始类型是按值传递的,例如,您可以在此处找到一个很好的讨论。

它使用与 Java 相同的原理。 它总是按值传递,你可以想象一个副本被传递。 对于原始类型,例如 Int,这很明显,这样的参数的值将被传递到 function 并且不会修改外部变量。 请注意,Kotlin 中的参数不能重新分配,因为它们的行为类似于 val

看起来在 lambda 案例中,围绕原始类型创建了某种包装器,并且该包装器保留了 state 并使值可变。

我搜索了有关将参数传递给 lambdas 的官方文档,但除了类似于我上面引用的讨论之外,找不到任何东西。

请提供任何描述在 lambda 中处理参数的参考资料。 问题不在于修复它,例如参见下面的代码,而是关于如何为原始类型创建、使用和生命周期的这些包装器的详细说明。 对 Google 或 InelliJ 官方文档的任何参考表示赞赏。 变量 i = 0

    arrayOf<CheckBox>(binding.chkBackup, binding.chkBackupEnc, binding.chkDrive,
            binding.chkDriveEnc, binding.chkMp3).forEach {
        val t = i++
        it.setOnClickListener { chk ->
            root.model.checkedBackups.value?.set(t, (chk as? CheckBox)?.isChecked ?: false)
        }

    }

在考虑了更多之后:在 lambda 案例中,包装器确实是必需的,因为通过将参数放在堆栈上的正常处理由于目标对象的延迟调用(在这种情况下为复选框)而无法正常工作。

但问题仍然存在:为什么所有目标对象都共享一个包装器? 为它们中的每一个创建一个包装器不是更合乎逻辑和更安全吗? 当前模式不直观且非常危险,因为可能存在其他问题,例如来自并发性。 对该共享包装线程的调用是否安全?

您正在越界访问元素。 arrays 和列表从零开始,这意味着数组的第一个元素位于索引 0 处,最后一个位于 position array.length-1

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM