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
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, e.g. you can find a good discussion here.
It uses the same principles like Java. It is always pass-by-value, you can imagine that a copy is passed. For primitive types, e.g. Int this is obvious, the value of such an argument will be passed into a function and the outer variable will not be modified. Please note that parameters in Kotlin cannot be reassigned since they act like 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.
I've searched official docs about passing parameters to lambdas, but couldn't find anything except discussions similar to what I've quoted above.
Please provide any references describing treating parameters in lambdas. The question was not about fixing it, e.g. 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. var 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).
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 e.g. coming from concurrency. Are calls to that shared wrapper thread safe?