4

Kind of a silly problem I'm facing here... Basically I'm in a For-loop and within this loop I'm always calling a function to make a button. But in this function I want to pass the loop iterator as it's changing to differentiate the buttons. But then it tells me I need to make the loop iterator "final" which means it doesn't change!

Well my problem will make more sense with some skeleton code:

for(int i = 0; i < appList.size(); i++) { 

                //some other stuff including creating the below button 'btn'

                btn.setOnClickListener(new View.OnClickListener() {
                    public void onClick(View v) {
                        //set some attribute based on i
                        //but when I do this it only sets based on the greatest value of i (!)
                    }
                });
}

Meh kinda silly problem I know... I'm new to Java though!

Cheers.

Pablo Fernandez
  • 103,170
  • 56
  • 192
  • 232
JDS
  • 16,388
  • 47
  • 161
  • 224
  • The comment saying you will set the attribute based on i using *the greatest value of i* seems inaccurate. You shouldn't be able to compile code containing your snippet due to the lack of keyword `final` indicated in the answers. You may have wrapped your integer in a final single element array, though, and you'd get the behavior you describe. – Atreys Jun 14 '11 at 15:37

2 Answers2

4
for(int i = 0; i < appList.size(); i++) { 

    final int _i = i;

    btn.setOnClickListener(new View.OnClickListener() {
        public void onClick(View v) {
            // use _i instead of i
        }
    });
}
Pablo Fernandez
  • 103,170
  • 56
  • 192
  • 232
1

Consider this bit of silly example code:

int i = 0;
obj.invokeThunkAfter10Seconds(new ThunkObject() {
    public void thunk() {
        System.out.println(i);
    }
}
i = 1;

The ThunkObject holds a reference to i, but i can change, which makes the output unpredictable and depends on when i is changed and when thunk is invoked.

Because Java doesn't have proper closures (by design, see Cannot refer to a non-final variable inside an inner class defined in a different method), you are explicitly not allowed to capture non-final (i.e. changable) variables like this.

Instead you must declare the variable final, meaning it's initialized and then is not changable. This means you can never change i after it has been captured by the inner class.

final int i = 0;
obj.invokeThunkAfter10Seconds(new ThunkObject() {
    public void thunk() {
        System.out.println(i);
    }
}
// Can't change i here. It will forever now be 0.

In your example you can't just set i to be final, because you do want to change the value on each loop iteration. Instead you can create a copy of the value in a final variable:

for(int i = 0; i < appList.size(); i++) { 
    //some other stuff including creating the below button 'btn'

    final int capturedI = i;
    btn.setOnClickListener(new View.OnClickListener() {
        public void onClick(View v) {
            //set some attribute based on capturedI
        }
    });
}
Community
  • 1
  • 1
ICR
  • 13,896
  • 4
  • 50
  • 78