简体   繁体   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)
        }

    }

Line #267 id the one with 'set' function call第 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)

There was a comment below about evaluating 'i' during the click event and this is really how it works, however, it contradicts to a common concept in many languages that primitive types are passed by values, eg you can find a good discussion here .下面有一条关于在单击事件期间评估“i”的评论,这实际上是它的工作原理,但是,它与许多语言中的一个常见概念相矛盾,即原始类型是按值传递的,例如,您可以在此处找到一个很好的讨论。

It uses the same principles like Java.它使用与 Java 相同的原理。 It is always pass-by-value, you can imagine that a copy is passed.它总是按值传递,你可以想象一个副本被传递。 For primitive types, eg Int this is obvious, the value of such an argument will be passed into a function and the outer variable will not be modified.对于原始类型,例如 Int,这很明显,这样的参数的值将被传递到 function 并且不会修改外部变量。 Please note that parameters in Kotlin cannot be reassigned since they act like val请注意,Kotlin 中的参数不能重新分配,因为它们的行为类似于 val

It looks like in lambda case some kind of wrapper is created around primitive type and that wrapper preserves the state and makes the value mutable.看起来在 lambda 案例中,围绕原始类型创建了某种包装器,并且该包装器保留了 state 并使值可变。

I've searched official docs about passing parameters to lambdas, but couldn't find anything except discussions similar to what I've quoted above.我搜索了有关将参数传递给 lambdas 的官方文档,但除了类似于我上面引用的讨论之外,找不到任何东西。

Please provide any references describing treating parameters in lambdas.请提供任何描述在 lambda 中处理参数的参考资料。 The question was not about fixing it, eg see code below, but about detailed explanation of how those wrappers are created, used and life-cycled for primitive types.问题不在于修复它,例如参见下面的代码,而是关于如何为原始类型创建、使用和生命周期的这些包装器的详细说明。 Any refs to official Google or InelliJ docs are appreciated.对 Google 或 InelliJ 官方文档的任何参考表示赞赏。 var i = 0变量 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)
        }

    }

After thinking more about this: a wrapper is really a necessity in lambda case, since normal treatment of params by putting them on a stack won't work due delayed calls by target objects (check boxes in this case).在考虑了更多之后:在 lambda 案例中,包装器确实是必需的,因为通过将参数放在堆栈上的正常处理由于目标对象的延迟调用(在这种情况下为复选框)而无法正常工作。

The question remains though: why a single wrapper is shared across all target objects?但问题仍然存在:为什么所有目标对象都共享一个包装器? Would not it be more logical and safe to create a wrapper for each of them?为它们中的每一个创建一个包装器不是更合乎逻辑和更安全吗? The current pattern is not intuitive and is very dangerous, since there could be other issues eg coming from concurrency.当前模式不直观且非常危险,因为可能存在其他问题,例如来自并发性。 Are calls to that shared wrapper thread safe?对该共享包装线程的调用是否安全?

you are accessing element out of bounds.您正在越界访问元素。 arrays and lists are zero based, which means first element of an array is at index 0, and the last is at position array.length-1 arrays 和列表从零开始,这意味着数组的第一个元素位于索引 0 处,最后一个位于 position array.length-1

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

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