Threads: Local variable defined in an enclosing scope must be final or effectively final

16,235

Solution 1

Your question itself has the answer to what you have asked.

Local variable tournament defined in an enclosing scope must be final or effectively final

If anonymous class is created inside any method then all the local variables defined in the method but outside the body of the anonymous class should be made final in case they are needed to be used in anonymous class.

public class OuterClass{
    int x,y;
    public void someMethod(){

         final int neededByAnonymousClass = x + y + 30;

         Runnable runnable = new Runnable(){
              // This is like we are creating a class which simply implements Runnable interface.
              // Scope of this class is inside the method someMethod()
              // run() method may be executed sometime later after the execution of someMethod() has completed.
             // all the local variables needs to be final in order they can be used later in time when run() gets executed.

             public void run(){
                 System.out.println(neededByAnonymousClass+ 40);
             }
         }
         Thread thread = new Thread(runnable); // passing the object of anonymous class, created above
         thread.start();
    }
}

So just make your all the local variables (defiend inside the scope of a method) as final which you wish to use inside the run() method of your local anonymous class (class without name). In case you wish to modify the value of a variable then do modifications first and then create the anonymous class. Create another final variable and initialize it with the modified value and use the same in the anonymous class.

I quote below from answer of another related question : Local variable needs to be declared final

It's a promise to yourself (and to the compiler) that the value of box won't change in the enclosing scope. The compiler will tell you if you break that promise.

The variable value will be used some time later after the execution of the method is completed. Hence local variables must be declared as final. The methods in the anonymous object are not executed in order they are written (sequentially)

Suppose below lines of code where we have a method : methodA() and it contains definition of an anonymous class.

[ line-1 : method A, line-2 method A, line-3 : anonymous class , Line 4 method of anonymous class, line 5 method A ] ==> Order of execution

Line 1, Line 2, Line 3 (just the object creation of the anonymous class), Line 5. Line 4 will be executed later when method on the object created of anonymous class is invoked.

Solution 2

So what is the problem.. a small example

String myString = new String("MyString");
Thread thr = new Thread() {
    public void run() {
        for (int i = 0; i < 5; i++) {
            System.out.println(myString);
        }
    }
}
thr.start();
myString = new String("AnotherString");

So what output would you expect here? Something like:

MyString
MyString
AnotherString
AnotherString
AnotherString

The problem is that you don't know when the myString variable is changed. This could happen after it was printed 0 times, after being printed 5 times or any time in between. Or in other words, this is not predictable and is very unlikely to be intended.
In order to have a defined behaviour, the variable you use in the Thread needs to be final:

final String myString = new String("MyString");
Thread thr = new Thread() {
    public void run() {
        for (int i = 0; i < 5; i++) {
            System.out.println(myString);
        }
    }
}
thr.start();
// now this following line is not valid anymore and will lead to a compile error
myString = new String("AnotherString");

Now we have a defined behaviour and we know that the output will be:

MyString
MyString
MyString
MyString
MyString
Share:
16,235
dabadaba
Author by

dabadaba

Updated on June 04, 2022

Comments

  • dabadaba
    dabadaba almost 2 years

    I have my main class running in the main method. It runs a process that potentially takes a huge amount of time to complete, so I created another method to stop that process: it simply raises a flag that makes the whole process stop:

    public void stopResolutionProcess() {
        stop = true;
    }
    

    This is the call that executes the big process:

    boolean solutionFound = tournament.solve();
    

    So right before it, I need to run a secondary thread to call stopResolutionProcess():

    Thread thread = new Thread(new Runnable() {
        @Override
        public void run() {
            System.out.println("Stop resolution process? (Y/N): ");
            String answer = sc.next();
            if (answer.equalsIgnoreCase("y")) {
                tournament.getSolver().stopResolutionProcess(); // error here
            }
        }
    });
    

    But I am getting an error in the last line. It says:

    Local variable tournament defined in an enclosing scope must be final or effectively final

    What approach should I take to solve this problem in order to test the method that stops the process?