views:

67

answers:

4

I have the following code. Which is "correct" and which I do not understand:

private static void updateGUI(final int i, final JLabel label) {
    SwingUtilities.invokeLater( 
        new Runnable() {
            public void run() {
                label.setText("You have " + i + " seconds.");
            }
        }
    );
}

I create a new instance of the Runnable class and then in the run method of this instance I use variables label and i. It works, but I do not understand why it work. Why the considered object sees values of these variables.

According to my understanding the code should look like that (and its wrong):

private static void updateGUI(final int i, final JLabel label) {
    SwingUtilities.invokeLater(new Runnable(i,label) {

        public Runnable(int i, JLabel label) {
            this.i = i;
            this.label = label;
        }

        public void run() {
            label.setText("You have " + i + " seconds.");
        }

    });
}

So, I would give the i and label variables to the constructor so the object can access them...

By the way, in the updateGUI I use final before the i and label. I think I used final because compiler wanted that. But I do not understand why.

+2  A: 

The compiler does this work for you (because you can't have constructors in anonymous classes). The rationale behind this is that label is in the region of visibility for the inner class. You can access the fields of the object or create non-anonymous inner classes that access final params and local variables too.

Ha
+2  A: 

Because the inner class sees all the variables of the outer class. The problem with the second example is that implementors of Runnable interface should have a constructor without a parameter.

rics
+4  A: 

final variables declared in the enclosing method can be accessed by an anonymous inner class in that method. If you would make the parameters non-final, you would see the compiler complain.

You can't declare a constructor in an anonymous inner class. People work around that sometimes by writing initializer blocks

new Runnable() {
  { /* this code is executed */ }

  public void run() {
    // ...
  }
};

I think the rationale of not being able to access non-final variables is because if you create your anonymous inner class object, and then would change the non-final variables - should the anonymous inner class use the updated value? How does it manage the case when the function terminates execution and the variable is destroyed? If it can only use final variables, the semantics are clear: It will take a snapshot of the final variable's values.

Johannes Schaub - litb
Indeed, the rationale is that the updateGUI method might terminate before the Runnable object is destroyed.
Eric Eijkelenboom
In fact, the values of the final variables are passed to the inner class constructor as extra hidden parameters, and they are stored by the inner class in hidden (final) attributes. You can see this if you look at the bytecodes using `javap`.
Stephen C
@Stephen, ah nice. Thanks for the tips.
Johannes Schaub - litb
A: 

The reason you can do this is because of the final keyword. This is a link explaining why. http://en.wikipedia.org/wiki/Final_%28Java%29

John