0
    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?

Oleg Gryb
  • 5,122
  • 1
  • 28
  • 40
  • 3
    The `i++` is evaluated when the checkbox is clicked, not when the click listener is added. – Henry Apr 06 '21 at 05:10
  • Just use `forEachIndexed` and replace the i with the index that forEachIndex will give. – Y.Kakdas Apr 06 '21 at 05:15
  • @Henry - you're probably right, but strange given that 'i' is a primitive type and should've been passed by value. In reality - there is an object with a state: I click on buttons first 5 times and everything is fine, but on sixth - it crashes, regardless on what button I clicked. Looks like 'i' is wrapped by an object. – Oleg Gryb Apr 06 '21 at 07:21

1 Answers1

-1

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

anusha anu
  • 19
  • 6
  • He wants to learn why he is accessing the element that is causing the exception, not explanation of index out of bound. – Y.Kakdas Apr 06 '21 at 05:27
  • @Y.Kakdas that is what im trying to say i=0 to array.length-1 but he trying to access nth element – anusha anu Apr 06 '21 at 07:32
  • Yes, but he needs to find out why his i goes beyond the bounds and obviously you do not give any opinion about that, i think he knows what index out of bound exception means. You did not answer that his underlying problem like Henry did in comments. – Y.Kakdas Apr 06 '21 at 07:40