[java] Difference between final and effectively final

I'm playing with lambdas in Java 8 and I came across warning local variables referenced from a lambda expression must be final or effectively final. I know that when I use variables inside anonymous class they must be final in outer class, but still - what is the difference between final and effectively final?

This question is related to java lambda inner-classes final java-8

The answer is


However, starting in Java SE 8, a local class can access local variables and parameters of the >enclosing block that are final or effectively final.

This didn't start on Java 8, I use this since long time. This code used (before java 8) to be legal:

String str = ""; //<-- not accesible from anonymous classes implementation
final String strFin = ""; //<-- accesible 
button.addActionListener(new ActionListener() {
    @Override
    public void actionPerformed(ActionEvent e) {
         String ann = str; // <---- error, must be final (IDE's gives the hint);
         String ann = strFin; // <---- legal;
         String str = "legal statement on java 7,"
                +"Java 8 doesn't allow this, it thinks that I'm trying to use the str declared before the anonymous impl."; 
         //we are forced to use another name than str
    }
);

When a lambda expression uses an assigned local variable from its enclosing space there is an important restriction. A lambda expression may only use local variable whose value doesn't change. That restriction is referred as "variable capture" which is described as; lambda expression capture values, not variables.
The local variables that a lambda expression may use are known as "effectively final".
An effectively final variable is one whose value does not change after it is first assigned. There is no need to explicitly declare such a variable as final, although doing so would not be an error.
Let's see it with an example, we have a local variable i which is initialized with the value 7, with in the lambda expression we are trying to change that value by assigning a new value to i. This will result in compiler error - "Local variable i defined in an enclosing scope must be final or effectively final"

@FunctionalInterface
interface IFuncInt {
    int func(int num1, int num2);
    public String toString();
}

public class LambdaVarDemo {

    public static void main(String[] args){             
        int i = 7;
        IFuncInt funcInt = (num1, num2) -> {
            i = num1 + num2;
            return i;
        };
    }   
}

'Effectively final' is a variable which would not give compiler error if it were to be appended by 'final'

From a article by 'Brian Goetz',

Informally, a local variable is effectively final if its initial value is never changed -- in other words, declaring it final would not cause a compilation failure.

lambda-state-final- Brian Goetz


public class LambdaScopeTest {
    public int x = 0;        
    class FirstLevel {
        public int x = 1;    
        void methodInFirstLevel(int x) {

            // The following statement causes the compiler to generate
            // the error "local variables referenced from a lambda expression
            // must be final or effectively final" in statement A:
            //
            // x = 99; 

        }
    }    
}

As others have said, a variable or parameter whose value is never changed after it is initialized is effectively final. In the above code, if you change the value of x in inner class FirstLevel then the compiler will give you the error message:

Local variables referenced from a lambda expression must be final or effectively final.


If you could add the final modifier to a local variable, it was effectively final.

Lambda expressions can access

  • static variables,

  • instance variables,

  • effectively final method parameters, and

  • effectively final local variables.

Source: OCP: Oracle Certified Professional Java SE 8 Programmer II Study Guide, Jeanne Boyarsky, Scott Selikoff

Additionally,

An effectively final variable is a variable whose value is never changed, but it isn’t declared with the final keyword.

Source: Starting Out with Java: From Control Structures through Objects (6th Edition), Tony Gaddis

Furthermore, don't forget the meaning of final that it is initialized exactly once before it is used for the first time.


A variable is final or effectively final when it's initialized once and it's never mutated in its owner class. And we can't initialize it in loops or inner classes.

Final:

final int number;
number = 23;

Effectively Final:

int number;
number = 34;

Note: Final and Effective Final are similar(Their value don't change after assignment) but just that effective Final variables are not declared with Keyword final.


The Effectively final variable is a local variable that is:

  1. Not defined as final
  2. Assigned to ONLY once.

While a final variable is a variable that is:

  1. declared with a final keyword.

According to the docs:

A variable or parameter whose value is never changed after it is initialized is effectively final.

Basically, if the compiler finds a variable does not appear in assignments outside of its initialization, then the variable is considered effectively final.

For example, consider some class:

public class Foo {

    public void baz(int bar) {
        // While the next line is commented, bar is effectively final
        // and while it is uncommented, the assignment means it is not
        // effectively final.

        // bar = 2;
    }
}

This variable below is final, so we can't change it's value once initialised. If we try to we'll get a compilation error...

final int variable = 123;

But if we create a variable like this, we can change it's value...

int variable = 123;
variable = 456;

But in Java 8, all variables are final by default. But the existence of the 2nd line in the code makes it non-final. So if we remove the 2nd line from the above code, our variable is now "effectively final"...

int variable = 123;

So.. Any variable that is assigned once and only once, is "effectively final".


I find the simplest way to explain "effectively final" is to imagine adding the final modifier to a variable declaration. If, with this change, the program continues to behave in the same way, both at compile time and at run time, then that variable is effectively final.


final is a variable declare with key word final , example:

final double pi = 3.14 ;

it remains final through out the program.

effectively final : any local variable or parameter that is assigned a value only once right now(or updated only once). It may not remain effectively final through out the program. so this means that effectively final variable might loose its effectively final property after immediately the time it gets assigned/updated at least one more assignment. example:

class EffectivelyFinal {

    public static void main(String[] args) {
        calculate(124,53);
    }

    public static void calculate( int operand1, int operand2){   
     int rem = 0;  //   operand1, operand2 and rem are effectively final here
     rem = operand1%2  // rem lost its effectively final property here because it gets its second assignment 
                       // operand1, operand2 are still effectively final here 
        class operators{

            void setNum(){
                operand1 =   operand2%2;  // operand1 lost its effectively final property here because it gets its second assignment
            }

            int add(){
                return rem + operand2;  // does not compile because rem is not effectively final
            }
            int multiply(){
                return rem * operand1;  // does not compile because both rem and operand1 are not effectively final
            }
        }   
   }    
}

Declaring a variable final or not declaring it final, but keeping it effectively final may result (depends on compiler) in different bytecode.

Let's have a look on a small example:

    public static void main(String[] args) {
        final boolean i = true;   // 6  // final by declaration
        boolean j = true;         // 7  // effectively final

        if (i) {                  // 9
            System.out.println(i);// 10
        }
        if (!i) {                 // 12
            System.out.println(i);// 13
        }
        if (j) {                  // 15
            System.out.println(j);// 16
        }
        if (!j) {                 // 18
            System.out.println(j);// 19
        }
    }

The corresponding bytecode of the main method (Java 8u161 on Windows 64 Bit):

  public static void main(java.lang.String[]);
    Code:
       0: iconst_1
       1: istore_1
       2: iconst_1
       3: istore_2
       4: getstatic     #16                 // Field java/lang/System.out:Ljava/io/PrintStream;
       7: iconst_1
       8: invokevirtual #22                 // Method java/io/PrintStream.println:(Z)V
      11: iload_2
      12: ifeq          22
      15: getstatic     #16                 // Field java/lang/System.out:Ljava/io/PrintStream;
      18: iload_2
      19: invokevirtual #22                 // Method java/io/PrintStream.println:(Z)V
      22: iload_2
      23: ifne          33
      26: getstatic     #16                 // Field java/lang/System.out:Ljava/io/PrintStream;
      29: iload_2
      30: invokevirtual #22                 // Method java/io/PrintStream.println:(Z)V
      33: return

The corresponding line number table:

 LineNumberTable:
   line 6: 0
   line 7: 2
   line 10: 4
   line 15: 11
   line 16: 15
   line 18: 22
   line 19: 26
   line 21: 33

As we see the source code at lines 12, 13, 14 doesn't appear in the byte code. That's because i is true and will not change it's state. Thus this code is unreachable (more in this answer). For the same reason the code at line 9 misses too. The state of i doesn't have to be evaluated since it is true for sure.

On the other hand though the variable j is effectively final it's not processed in the same way. There are no such optimizations applied. The state of j is evaluated two times. The bytecode is the same regardless of j being effectively final.


Effective final topic is described in JLS 4.12.4 and the last paragraph consists a clear explanation:

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.


Examples related to java

Under what circumstances can I call findViewById with an Options Menu / Action Bar item? How much should a function trust another function How to implement a simple scenario the OO way Two constructors How do I get some variable from another class in Java? this in equals method How to split a string in two and store it in a field How to do perspective fixing? String index out of range: 4 My eclipse won't open, i download the bundle pack it keeps saying error log

Examples related to lambda

Java 8 optional: ifPresent return object orElseThrow exception How to properly apply a lambda function into a pandas data frame column What are functional interfaces used for in Java 8? Java 8 lambda get and remove element from list Variable used in lambda expression should be final or effectively final Filter values only if not null using lambda in Java8 forEach loop Java 8 for Map entry set Java 8 Lambda Stream forEach with multiple statements Java 8 stream map to list of keys sorted by values Task.Run with Parameter(s)?

Examples related to inner-classes

Difference between final and effectively final Is not an enclosing class Java Nested or Inner Class in PHP Java - No enclosing instance of type Foo is accessible Can we create an instance of an interface in Java? Why would one use nested classes in C++? Getting hold of the outer class object from the inner class object Nested classes' scope? Java: Static vs inner class Why Would I Ever Need to Use C# Nested Classes

Examples related to final

Difference between final and effectively final How does the "final" keyword in Java work? (I can still modify an object.) when exactly are we supposed to use "public static final String"? What is the purpose of the "final" keyword in C++11 for functions? In Java, what purpose do the keywords `final`, `finally` and `finalize` fulfil? Static Final Variable in Java What is the point of "final class" in Java? Change private static final field using Java reflection Why is there no Constant feature in Java? final keyword in method parameters

Examples related to java-8

Default interface methods are only supported starting with Android N Class has been compiled by a more recent version of the Java Environment Why is ZoneOffset.UTC != ZoneId.of("UTC")? Modify property value of the objects in list using Java 8 streams How to use if-else logic in Java 8 stream forEach Android Studio Error: Error:CreateProcess error=216, This version of %1 is not compatible with the version of Windows you're running Error:could not create the Java Virtual Machine Error:A fatal exception has occured.Program will exit What are functional interfaces used for in Java 8? java.time.format.DateTimeParseException: Text could not be parsed at index 21 Java 8 lambda get and remove element from list