[java] Why are only final variables accessible in anonymous class?

  1. a can only be final here. Why? How can I reassign a in onClick() method without keeping it as private member?

    private void f(Button b, final int a){
        b.addClickHandler(new ClickHandler() {
    
            @Override
            public void onClick(ClickEvent event) {
                int b = a*5;
    
            }
        });
    }
    
  2. How can I return the 5 * a when it clicked? I mean,

    private void f(Button b, final int a){
        b.addClickHandler(new ClickHandler() {
    
            @Override
            public void onClick(ClickEvent event) {
                 int b = a*5;
                 return b; // but return type is void 
            }
        });
    }
    

This question is related to java event-handling anonymous-class

The answer is


Maybe this trick gives u an idea

Boolean var= new anonymousClass(){
    private String myVar; //String for example
    @Overriden public Boolean method(int i){
          //use myVar and i
    }
    public String setVar(String var){myVar=var; return this;} //Returns self instane
}.setVar("Hello").method(3);

When an anonymous inner class is defined within the body of a method, all variables declared final in the scope of that method are accessible from within the inner class. For scalar values, once it has been assigned, the value of the final variable cannot change. For object values, the reference cannot change. This allows the Java compiler to "capture" the value of the variable at run-time and store a copy as a field in the inner class. Once the outer method has terminated and its stack frame has been removed, the original variable is gone but the inner class's private copy persists in the class's own memory.

(http://en.wikipedia.org/wiki/Final_%28Java%29)


Methods within an anonomyous inner class may be invoked well after the thread that spawned it has terminated. In your example, the inner class will be invoked on the event dispatch thread and not in the same thread as that which created it. Hence, the scope of the variables will be different. So to protect such variable assignment scope issues you must declare them final.


As Jon has the implementation details answer an other possible answer would be that the JVM doesn't want to handle write in record that have ended his activation.

Consider the use case where your lambdas instead of being apply, is stored in some place and run later.

I remember that in Smalltalk you would get an illegal store raised when you do such modification.


Java final variable inside an inner class[About]

inner class can use only

  1. reference from outer class
  2. final local variables from out of scope which are a reference type (e.g. Object...)
  3. value(primitive) (e.g. int...) type can be wrapped by a final reference type. IntelliJ IDEA can help you covert it to one element array

When a non static nested (inner class) is generated by compiler - a new class - <OuterClass>$<InnerClass>.class is created and bound parameters are passed into constructor[Local variable on stack]. It is similar to closure

final variable is a variable which can not be reassign. final reference variable still can be changed by modifying a state

If it was be possible it would be weird because as a programmer you could make like this

//Not possible 
private void foo() {

    MyClass myClass = new MyClass(); //Case 1. address 1
    int a = 5; //Case 2. a = 5

    Button button = new Button();

    //just as an example
    button.addClickHandler(new ClickHandler() {
        
        @Override
        public void onClick(ClickEvent event) {

            /*
            myClass.something(); //<- what is the address ?
            int b = a; //<- 5 or 10 ?

            //illusion that next changes are visible for Outer class
            myClass = new MyClass();
            a = 15;
            */
        }
    });

    myClass = new MyClass(); //Case 1. address 2
    int a = 10; //Case 2. a = 10
}

Try this code,

Create Array List and put value inside that and return it :

private ArrayList f(Button b, final int a)
{
    final ArrayList al = new ArrayList();
    b.addClickHandler(new ClickHandler() {

         @Override
        public void onClick(ClickEvent event) {
             int b = a*5;
             al.add(b);
        }
    });
    return al;
}

private void f(Button b, final int a[]) {

    b.addClickHandler(new ClickHandler() {

        @Override
        public void onClick(ClickEvent event) {
            a[0] = a[0] * 5;

        }
    });
}

There is a trick that allows anonymous class to update data in the outer scope.

private void f(Button b, final int a) {
    final int[] res = new int[1];
    b.addClickHandler(new ClickHandler() {
        @Override
        public void onClick(ClickEvent event) {
            res[0] = a * 5;
        }
    });

    // But at this point handler is most likely not executed yet!
    // How should we now res[0] is ready?
}

However, this trick is not very good due to the synchronization issues. If handler is invoked later, you need to 1) synchronize access to res if handler was invoked from the different thread 2) need to have some sort of flag or indication that res was updated

This trick works OK, though, if anonymous class is invoked in the same thread immediately. Like:

// ...

final int[] res = new int[1];
Runnable r = new Runnable() { public void run() { res[0] = 123; } };
r.run();
System.out.println(res[0]);

// ...

To understand the rationale for this restriction, consider the following program:

public class Program {

    interface Interface {
        public void printInteger();
    }
    static Interface interfaceInstance = null;

    static void initialize(int val) {
        class Impl implements Interface {
            @Override
            public void printInteger() {
                System.out.println(val);
            }
        }
        interfaceInstance = new Impl();
    }

    public static void main(String[] args) {
        initialize(12345);
        interfaceInstance.printInteger();
    }
}

The interfaceInstance remains in memory after the initialize method returns, but the parameter val does not. The JVM can’t access a local variable outside its scope, so Java makes the subsequent call to printInteger work by copying the value of val to an implicit field of the same name within interfaceInstance. The interfaceInstance is said to have captured the value of the local parameter. If the parameter weren’t final (or effectively final) its value could change, becoming out of sync with the captured value, potentially causing unintuitive behavior.


You can create a class level variable to get returned value. I mean

class A {
    int k = 0;
    private void f(Button b, int a){
        b.addClickHandler(new ClickHandler() {
        @Override
        public void onClick(ClickEvent event) {
            k = a * 5;
        }
    });
}

now you can get value of K and use it where you want.

Answer of your why is :

A local inner class instance is tied to Main class and can access the final local variables of its containing method. When the instance uses a final local of its containing method, the variable retains the value it held at the time of the instance's creation, even if the variable has gone out of scope (this is effectively Java's crude, limited version of closures).

Because a local inner class is neither the member of a class or package, it is not declared with an access level. (Be clear, however, that its own members have access levels like in a normal class.)


The reason why the access has been restricted only to the local final variables is that if all the local variables would be made accessible then they would first required to be copied to a separate section where inner classes can have access to them and maintaining multiple copies of mutable local variables may lead to inconsistent data. Whereas final variables are immutable and hence any number of copies to them will not have any impact on the consistency of data.


An anonymous class is an inner class and the strict rule applies to inner classes (JLS 8.1.3):

Any local variable, formal method parameter or exception handler parameter used but not declared in an inner class must be declared final. Any local variable, used but not declared in an inner class must be definitely assigned before the body of the inner class.

I haven't found a reason or an explanation on the jls or jvms yet, but we do know, that the compiler creates a separate class file for each inner class and it has to make sure, that the methods declared on this class file (on byte code level) at least have access to the values of local variables.

(Jon has the complete answer - I keep this one undeleted because one might interested in the JLS rule)


Java anonymous class is very similar to Javascript closure, but Java implement that in different way. (check Andersen's answer)

So in order not to confuse the Java Developer with the strange behavior that might occur for those coming from Javascript background. I guess that's why they force us to use final, this is not the JVM limitation.

Let's look at the Javascript example below:

var add = (function () {
  var counter = 0;

  var func = function () {
    console.log("counter now = " + counter);
    counter += 1; 
  };

  counter = 100; // line 1, this one need to be final in Java

  return func;

})();


add(); // this will print out 100 in Javascript but 0 in Java

In Javascript, the counter value will be 100, because there is only one counter variable from the beginning to end.

But in Java, if there is no final, it will print out 0, because while the inner object is being created, the 0 value is copied to the inner class object's hidden properties. (there are two integer variable here, one in the local method, another one in inner class hidden properties)

So any changes after the inner object creation (like line 1), it will not affect the inner object. So it will make confusion between two different outcome and behaviour (between Java and Javascript).

I believe that's why, Java decide to force it to be final, so the data is 'consistent' from the beginning to end.


Well, in Java, a variable can be final not just as a parameter, but as a class-level field, like

public class Test
{
 public final int a = 3;

or as a local variable, like

public static void main(String[] args)
{
 final int a = 3;

If you want to access and modify a variable from an anonymous class, you might want to make the variable a class-level variable in the enclosing class.

public class Test
{
 public int a;
 public void doSomething()
 {
  Runnable runnable =
   new Runnable()
   {
    public void run()
    {
     System.out.println(a);
     a = a+1;
    }
   };
 }
}

You can't have a variable as final and give it a new value. final means just that: the value is unchangeable and final.

And since it's final, Java can safely copy it to local anonymous classes. You're not getting some reference to the int (especially since you can't have references to primitives like int in Java, just references to Objects).

It just copies over the value of a into an implicit int called a in your anonymous class.