Effectively Final Variable in Java with Examples

In Java, an effective final variable is not declared with the final keyword but it's value is not changed after its initial assignment. This concept is essential when dealing with lambda expressions and anonymous inner classes, which can only access local variables that are either final or effectively final.

An effective final variable is a local variable that is:

  • Not explicitly declared as final.
  • Assigned a value only once.

Though it is not marked with the final keyword, it behaves like a final variable because its value is never modified after the initial assignment. An effectively final variable's property can be lost if it is assigned a new value after the initial assignment.

File Name: EffectiveFinalExample.java

Output:

 
ERROR!
/tmp/L86saMCavD/EffectiveFinalExample.java:27: error: local variables referenced from an inner class must be final or effectively final
                operand1 = operand2 % 2; // Compilation error
ERROR!
                ^
/tmp/L86saMCavD/EffectiveFinalExample.java:33: error: local variables referenced from an inner class must be final or effectively final
                return rem + operand2;
                       ^
/tmp/L86saMCavD/EffectiveFinalExample.java:39: error: local variables referenced from an inner class must be final or effectively final
                return rem * operand1;
                       ^
3 errors   

Lambda Expression Capture Values

When using a lambda expression in Java, there is a significant constraint on the local variables it can access. Specifically, a lambda expression can only work with local variables whose values remain unchanged. This rule, known as variable capture, means that lambda expressions capture the values of variables, not the variables themselves.

Local variables that lambda expressions use must be effectively final. This term refers to variables that are assigned a value only once and do not get modified afterward. Although these variables do not need to be explicitly declared as final, doing so can clarify their intended immutability.

Example and Explanation

Consider a scenario where we have a local variable i initialized with a value of 7. If we attempt to change the value of i within the lambda expression, the compiler will produce an error. The error message will be: "Local variable i defined in an enclosing scope must be final or effectively final." This occurs because the lambda expression requires the local variable to maintain a consistent value throughout its execution.

File Name: EffectiveFinalExample.java

Output:

 
ERROR!
/tmp/vGTOxU9wYP/EffectiveFinalExample.java:20: error: local variables referenced from a lambda expression must be final or effectively final
ERROR!
            i = num1 + num2;
            ^
/tmp/vGTOxU9wYP/EffectiveFinalExample.java:22: error: local variables referenced from a lambda expression must be final or effectively final
            return i;
                   ^
2 errors   

Advantages of Effectively Final Variables

Cleaner Code: Effectively final variables help streamline your code, especially with lambda expressions and anonymous inner classes. You don't have to declare every local variable as final, which reduces boilerplate and makes your code more concise.

Encapsulation: By utilizing effectively final variables, you encourage encapsulating variables within the smallest scope possible. This approach minimizes the risk of unintended modifications from other parts of the code, thus maintaining better control over variable access.

Concurrency: In multi-threaded scenarios, effectively final variables offer added safety. Since these variables are essentially read-only, they mitigate the risk of data races and synchronization issues, leading to more reliable concurrent programming.

Best Practice

  1. Limit Variable Scope: Define variables within the smallest scope necessary to minimize their visibility and prevent accidental modifications.
  2. Explicitly Use final When Needed: If a variable is intended to remain unchanged, explicitly declare it as final to clearly convey this intent in your code.
  3. Prefer Immutability: Whenever possible, use immutable objects and effectively final variables. This approach leads to safer and more predictable code by avoiding unintended changes.
  4. Examine Lambda Expressions: Ensure that all variables accessed within lambda expressions and anonymous inner classes are either effectively final or declared as final.