8

I can`t use the variable "i" in the method run(). Is there any way to do it ?

public class Main {

    public static void main(String[] args) {

         final int M = 100;
         final int N = 4;
         final int[] array = new int[M];

         for(int b = 0; b < M; b++) array[b] = b;

         for( int i = 0; i < N; i++) {
             new Thread(new Runnable() {
                 public void run() {
                         for(int a = i*(M/N);a < (i+1)*(M/N); a++)
                             System.out.println("Thread "+i+":"+array[a]); 
                         // i -> cannot refer to a non-final variable inside an inner class defined in a different method
                     }
             }).start();
         } 



    }   
}
PearsonArtPhoto
  • 38,970
  • 17
  • 111
  • 142
user2976091
  • 233
  • 3
  • 4
  • 11
  • http://stackoverflow.com/questions/1299837/cannot-refer-to-a-non-final-variable-inside-an-inner-class-defined-in-a-differen – Maroun Nov 10 '13 at 12:19
  • 2
    If you google your error, you'll get at least 100 answers. – Maroun Nov 10 '13 at 12:20

4 Answers4

23

You can declare a final variable p which will take the value of i at each iteration.

    for( int i = 0; i < N; i++) {
       final int p = i;
       new Thread(new Runnable() {
             public void run() {
                  for(int a = p*(M/N);a < (p+1)*(M/N); a++)
                  System.out.println("Thread "+p+":"+array[a]);
             }
        }).start();
     } 

Why I need to have a final declaration ?

Community
  • 1
  • 1
Alexis C.
  • 91,686
  • 21
  • 171
  • 177
  • 1
    +1 But please add the reason why non-final variable cannot be accessed. – Juned Ahsan Nov 10 '13 at 12:18
  • I have a quiet common problem: I want to start a thread within another thread but i have to make the thread final - does this solution works aswell for my specific case or do i have to find another "work-around"? – Yannic Hansen Oct 17 '14 at 15:49
9

That's right. It is a limitation of Java inner classes! An inner class is not allowed to access an instance variable in an enclosing scope unless that variable is declared as final.

So you need to implement that code like this ...

    for (int i = 0; i < N; i++) {
        final int ii = i;
        new Thread(new Runnable() {
             public void run() {
                 for(int a = ii*(M/N);a < (ii+1)*(M/N); a++)
                     System.out.println("Thread "+ii+":"+array[a]);
             }
        }).start();
    }

The reason for this limitation is to avoid the need for Java to implement closures. Since ii is final, the compiler can implement the above by passing a copy of the value of ii to the anonymous inner class when it instantiates it. This is stored in a hidden variable ... so that it can be used after the enclosing method call completes.

Without this restriction, a (hypothetical) Java compiler would need to create a heap object to hold the i variable. (Or at least it would if it couldn't optimize the object away ...) That's what closures are all about ... at the implementation level.


The other point that people sometimes miss is that this particular algorithm would be incorrect if Java allowed you to access the non-final i. Why? Because by the time that the threads actually started, the outer loop would most likely have finished, and the threads would all see i with the value N. That is NOT what you are trying to achieve here.


UPDATE starting with Java 8, the outer instance variable only needs to be effectively final.

Stephen C
  • 698,415
  • 94
  • 811
  • 1,216
  • Even a reference variable would have to be declared final.But reference variables know their actual object reference only at run time and in which case how is the compiler able to optimize something ? – crackerplace Apr 04 '16 at 13:53
  • 1) This is not an optimization problem. 2) What the compiler actually does is to pass the value of the final / effectively variable's value as a parameter to the anonymous class constructor. At runtime. – Stephen C Apr 04 '16 at 14:13
3

Try:

for( int i = 0; i < N; i++) {
    final currentIndex = i; // declare final here
    new Thread(new Runnable() {
        public void run() {
            for(int a = currentIndex*(M/N) ; a < (currentIndex+1)*(M/N) ; a++)
                System.out.println("Thread " + currentIndex + ":" + array[a]); 
         }
     }).start();
} 
Jean Logeart
  • 52,687
  • 11
  • 83
  • 118
3

Thats the limitation of Java Inner Class as Java doesnot support true closures

Also check out this Cannot refer to a non-final variable inside an inner class defined in a different method for details.

You may try like this:

for (int i = 0; i < N; i++) {
        final int x = i;
        new Thread(new Runnable() {
             public void run() {
                 for(int a = x*(M/N);a < (x+1)*(M/N); a++)
                     System.out.println("Thread "+x+":"+array[a]);
             }
        }).start();
    } 
Community
  • 1
  • 1
Rahul Tripathi
  • 168,305
  • 31
  • 280
  • 331