3

Given the following inner class (IsSomething) within a method:

public class InnerMethod {

private int x;


public class Something {
    private int y;

    public void printMyNumber(double x)
    {
        class IsSomething extends Something {

            public void print() {
                System.out.println(x);
            }

        }
    }
}

}

Why does the X variable has to be FINAL to make it work..? (I'm talking ofc about the X parameter of the "printMyNumber" function.)

Rouki
  • 2,239
  • 1
  • 24
  • 41

3 Answers3

1

The methods in an anonymous class don't really have access to local variables and method parameters. Rather, when an object of the anonymous class is instantiated, copies of the final local variables and method parameters referred to by the object's methods are stored as instance variables in the object. The methods in the object of the anonymous class really access those hidden instance variables.

From the JLS :

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 (§16) before the body of the inner class.

AllTooSir
  • 48,828
  • 16
  • 130
  • 164
  • Ok, so the compiler does keep that final variable inside the current object. right? – Rouki Apr 15 '13 at 05:43
  • The value is copied into the instance of the anonymous inner class , so once copied the code inside the method but outside the class shouldn't modify it or the values will not be in sync . – AllTooSir Apr 15 '13 at 05:44
  • Off topic question. does an inner class automatically extends the class within it? – Rouki Apr 15 '13 at 05:46
  • No , they are both different concepts altogether . – AllTooSir Apr 15 '13 at 05:47
  • @noob: I wanted to ask how does the extending of the outer class works, like in this question? I mean how the inner class can have a definition of itself which is in the outer class.? – me_digvijay Apr 15 '13 at 05:54
1

The difference is between local variables vs class member variables. A member variable exists during the lifetime of the enclosing object, so it can be referenced by the inner class instance. A local variable, however, exists only during the method invocation, and is handled differently by the compiler, in that an implicit copy of it is generated as the member of the inner class. Without declaring the local variable final, one could change it, leading to subtle errors due to the inner class still referring to the original value of that variable.

Final local variables

There are two reasons I know for making a local variable or a parameter final. The first reason is that you don't want your code changing the local variable or parameter. It is considered by many to be bad style to change a parameter inside a method as it makes the code unclear. As a habit, some programmers make all their parameters "final" to prevent themselves from changing them. I don't do that, since I find it makes my method signature a bit ugly.

The second reason comes in when we want to access a local variable or parameter from within an inner class. This is the actual reason, as far as I know, that final local variables and parameters were introduced into the Java language in JDK 1.1.

public class Access1 {
  public void f() {
    final int i = 3;
    Runnable runnable = new Runnable() {
    public void run() {
      System.out.println(i);
    }
    };
  }
}

Inside the run() method we can only access i if we make it final in the outer class. To understand the reasoning, we have to

look at what the compiler does. It produces two files, Access1.class and Access1$1.class. When we decompile them with JAD, we get:

public class Access1 {
  public Access1() {}
  public void f() {
    Access1$1 access1$1 = new Access1$1(this);
  }
}

and

class Access1$1 implements Runnable {
  Access1$1(Access1 access1) {
    this$0 = access1;
  }
  public void run() {
    System.out.println(3);
  }
  private final Access1 this$0;
}

Since the value of i is final, the compiler can "inline" it into the inner

class. It perturbed me that the local variables had to be final to be accessed by the inner class until I saw the above.

When the value of the local variable can change for different instances of the inner class, the compiler adds it as a data member of the inner class and lets it be initialised in the constructor. The underlying reason behind this is that Java does not have pointers, the way that C has.

Consider the following class:

public class Access2 {
  public void f() {
    for (int i=0; i<10; i++) {
    final int value = i;
    Runnable runnable = new Runnable() {
      public void run() {
        System.out.println(value);
      }
    };
    }
  }
} 

The problem here is that we have to make a new local data member each time we go through the for loop, so a thought I had today

while coding, was to change the above code to the following:

public class Access3 {
  public void f() {
    Runnable[] runners = new Runnable[10];
    for (final int[] i={0}; i[0]<runners.length; i[0]++) {
    runners[i[0]] = new Runnable() {
      private int counter = i[0];
      public void run() {
        System.out.println(counter);
      }
    };
    }
    for (int i=0; i<runners.length; i++)
    runners[i].run();
  }
  public static void main(String[] args) {
    new Access3().f();
  }
}

We now don't have to declare an additional final local variable. In fact, is it not perhaps true that

int[] i is like a common C pointer to an int? It took me 4 years to see this, but I'd like to hear from you if you have heard this idea somewhere else.

Achintya Jha
  • 12,735
  • 2
  • 27
  • 39
  • So by declaring it FINAL. what does the compiler do? keeping it..? won't it get destroyed at the end of the function scope? – Rouki Apr 15 '13 at 05:40
  • Read it all :) nice. about this line : System.out.println(3); Does it just really replace the I final variable with 3? inlining it? – Rouki Apr 15 '13 at 06:01
  • @Rouki I dont think it replaces i with 3.final 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. – Achintya Jha Apr 15 '13 at 06:16
0

This is because the lifetime of an instance of a local class can be much longer than the execution of the method in which the class is defined. For this reason, a local class must have a private internal copy of all local variables it uses (these copies are automatically generated by the compiler). The only way to ensure that the local variable and the private copy are always the same is to insist that the local variable is final.

Jaydeep Rajput
  • 3,605
  • 17
  • 35