2

Possible Duplicate:
Cannot refer to a non-final variable inside an inner class defined in a different method
Why are only final variables accessible in anonymous class?

Looked over in SO and google looking for an answer to this question but could not find any.

I have the following code:

MyClass variable = new MyClass();
Button b = new Button();
b.addActionListener(new ActionListener() {
   public void actionPerformed(ActionEvent e){
         System.out.println("You clicked the button");
         variable.doSomething();
   }
});

The compiler returns this:

local variable variable is accessed from within inner class; needs to be declared final

What are the technical reasons why variable must be final?

Community
  • 1
  • 1
GETah
  • 20,922
  • 7
  • 61
  • 103
  • http://stackoverflow.com/questions/3910324/why-inner-classes-require-final-outer-instance-variables-java, http://stackoverflow.com/questions/1299837/cannot-refer-to-a-non-final-variable-inside-an-inner-class-defined-in-a-differen, etc. – Dave Newton Jun 10 '12 at 15:10
  • Thanks for the links. There is no explanation as to why the compiler is designed that way. Why wouldn't inner classes have references to outclasses and access the variables when needed instead of having a reference when first instantiated? – GETah Jun 10 '12 at 15:14
  • @GETah: If the variable were a member, you'd have a point. But it's a local. You can't rely on the stack frame it's defined in to still exist, and there's no such thing in Java as a reference to a variable. So if you want to use it, you pretty much have to make a copy. – cHao Jun 10 '12 at 15:54

2 Answers2

6

It is because you are using anonymous inner class. What happens is that compiler creates class for you. It calls it as your outer class and adds $ and number, e.g. $, $2 etc.

The class has reference to outer class initialized automatically, so its instance can use methods and fields of outer class.

But your class is anonymous. It kind of defined inside method and can use its internal variables defined before this anonymous class. The question is "how can it do it?" Really, you cannot refer to "instance of running method" to access its variables. The answer is that all method variables referenced from anonymous inner class are copied to the anonymous inner class . Therefore the variables are required to be final: otherwise somebody can change their values from the outer class and the changes will not be visible into the inner class.

AlexR
  • 114,158
  • 16
  • 130
  • 208
0

This is because the inner class executes code only when an event is triggered. If the variable is not declared final, then the MyClass object referenced in variable can change, and the inner class will not know which Object it is supposed to be referencing if it needs the MyClass object.

Thus, it should be declared final so this reference will never change.

Imagine this without the final keyword:

variable is referencing MyClass Object with hashCode(): 12345 inner class is created, and variable in inner class is referencing MyClass Object with hashCode(): 12345

variable is changed to be now referencing MyClass Object with hashCode(): abcde variable in inner class is still referencing MyClass Object with hashCode(): 12345

How does Java run code from here when event is triggered? Use which MyClass object?

Now with the final keyword:

variable is referencing MyClass Object with hashCode(): 12345 inner class is created, and variable in inner class is referencing MyClass Object with hashCode(): 12345

Due to the final keyword, the reference cannot be changed.

Java always know which MyClass object to call when event is triggered. No problems.

Menma
  • 799
  • 1
  • 9
  • 35
Lai Xin Chu
  • 2,462
  • 15
  • 29
  • So the compiler is not clever enough to know that it is setting somewhere in the outer class? – GETah Jun 10 '12 at 15:12
  • I don't think that this answer is correct. The "final" requirement applies to all variables, not only those that are references. – Jochen Jun 10 '12 at 15:13
  • @Jochen It is correct. The same problem can occur with primitives. Threads cache variables, both primitive and reference. – corsiKa Jun 10 '12 at 15:14
  • same problem. if the primitive variable is changed outside the inner class, then it wont be reflected in the inner class. thus, it should be declared final. – Lai Xin Chu Jun 10 '12 at 15:16
  • The inner class could have had a reference to the outerclass instead and use that one to access the proper `variable` – GETah Jun 10 '12 at 15:16
  • yes GETah. that is one practice to ensure that they always have a proper reference to the same object – Lai Xin Chu Jun 10 '12 at 15:16
  • @GETah: You're accessing a local variable, not a member. Nothing guarantees that variable will even *exist* when the event handler finally runs, unless you make it `final`. Java's closures basically work by copying variables they need, which wouldn't provide the illusion of accessing them from another function unless the variables can't change. – cHao Jun 10 '12 at 15:17
  • @corsiKa The problem mentioned in the question above regards (method) local variables. This answer addresses an entirely different problem, and it is not at all necessary that fields of a class have to be final for them to be accessible by nested classes. – Jochen Jun 10 '12 at 15:22
  • @Jochen: This answer addresses the very question being asked. Java's "closures" work by constructing an anonymous class and copying the useful final locals into it. The reason that they work at all is because the variables won't change without breaking the rules of Java, so there's no worry about keeping the two copies sync'ed up. – cHao Jun 10 '12 at 15:31
  • BTW...am i the only one getting the impression that the Java folks are just phoning it in now? It seems like they always go for the "solution" that means the least work for them, even if it's a relatively crappy solution... – cHao Jun 10 '12 at 15:36