5

Before java 8 an inner class could access outer objects only if they were declared final. However now when I run example code (from below) on javaSE 1.8 there is no compilation error and program runs fine.

Why did they change that and how does It work now?

Example code from java 7 tutorial:

public class MOuter {
    private int m = (int) (Math.random() * 100);

    public static void main(String[] args) {
        MOuter that = new MOuter();
        that.go((int) (Math.random() * 100), (int) (Math.random() * 100));
    }

    public void go(int x, final int y){
        int a = x + y;
        final int b = x - y;

        class MInner{
            public void method(){
                System.out.println("m is "+m);
                System.out.println("x is "+x); // supposedly illegal - 'x' not final
                System.out.println("y is: "+y);
                System.out.println("a is "+a); // supposedly illegal? - 'a' not final
            }
        }       
        MInner that = new MInner();
        that.method();
    }   
}
lxg
  • 12,375
  • 12
  • 51
  • 73
Marco
  • 582
  • 1
  • 6
  • 17
  • 1
    What JB said. It's now "effectively final." It was added to better support lambdas in Java 8 (which are like closures but a little different). – markspace Oct 27 '14 at 20:11
  • 2
    [Difference between final and effectively final](http://stackoverflow.com/questions/20938095/difference-between-final-and-effectively-final) – alex Oct 27 '14 at 20:18

2 Answers2

7

In Java 7 the the concept of effectively final was introduced to support the "more precise rethrow" feature, and its scope was expanded in Java 8 to cover local variables that are assigned only once, but not actually declared final. These can be captured and used in lambda bodies or inner classes just as if they were declared final.

This is covered in section §4.12.4 of the Java Language Specification:

Certain variables that are not declared final may instead be considered effectively final.

A local variable or a method, constructor, lambda, or exception parameter is effectively final if it is not declared final but it never occurs as the left hand operand of an assignment operator (§15.26) or as the operand of a prefix or postfix increment or decrement operator (§15.14, §15.15).

In addition, a local variable whose declaration lacks an initializer is effectively final if all of the following are true:

  • It is not declared final.

  • Whenever it occurs as the left-hand operand of an assignment operator, it is definitely unassigned and not definitely assigned before the assignment; that is, it is definitely unassigned and not definitely assigned after the right-hand operand of the assignment (§16 (Definite Assignment)).

  • It never occurs as the operand of a prefix or postfix increment or decrement operator.

If a variable is effectively final, adding the final modifier to its declaration will not introduce any compile-time errors. Conversely, a local variable or parameter that is declared final in a valid program becomes effectively final if the final modifier is removed.

David Conrad
  • 15,432
  • 2
  • 42
  • 54
  • 3
    Actually, the notion of *effectively final* was added in Java 7, and used in the "more precise rethrow" feature. Its scope was expanded in Java 8 to cover local variables captured by lambdas or inner classes. – Brian Goetz Oct 27 '14 at 21:03
3

It's still the same rule, except the compiler doesn't force you to explicitely define the variable as final anymore. If it is effectively final, you can access it. If it's not (i.e. the compiler detects that the variable is reassigned), then it doesn't compile.

JB Nizet
  • 678,734
  • 91
  • 1,224
  • 1,255