Codementor Events

Variable used in Lambda Expression Should be Final or Effectively Final

Published Jun 29, 2022
Variable used in Lambda Expression Should be Final or Effectively Final

Anonymous Classes in java are inner classes without a name, these are used in java to override the method without actually creating a subclass. Anonymous Inner Class can use the local variable of other class.

package org.wesome.java8;

abstract class Fuji {
    abstract void loveApple();
}

class Apple {
    public static void main(String[] args) {
        String appleName = "Fuji";

        Fuji fuji = new Fuji() {
            @Override
            void loveApple() {
                System.out.println("i love " + appleName + " apple.");
            }
        };

        fuji.loveApple();
    }
}

Anonymous Inner Class can use the local variable of the outer class but it cannot modify them. the below code will result in Compile Time Exception.

package org.wesome.java8;

abstract class Fuji {
    abstract void loveApple();
}

class Apple {
    public static void main(String[] args) {
        String appleName = "Fuji";

        Fuji fuji = new Fuji() {
            @Override
            void loveApple() {
                appleName = appleName.toUpperCase();
                System.out.println("i love " + appleName + " apple.");
            }
        };

        fuji.loveApple();
    }
}

Variable 'appleName' is accessed from within inner class, needs to be final or effectively final

The Lambda Expression was introduced by Java to reduce the complexity of the Anonymous Inner Class, but it carries the same functionality as the Anonymous Inner Class, the Lambda Expression can access but cannot modify the Local Variables.

package org.wesome.java8;

import java.util.stream.IntStream;

class Apple {
    public static void main(String[] args) {
        int temp = 1;
        IntStream.rangeClosed(1, 5).forEach(i -> {
            System.out.println("value of temp is = " + temp);
        });
    }
}

The below code trying to update the temp variable in the lambda expression will result in a compilation error.

package org.wesome.java8;

import java.util.stream.IntStream;

class Apple {
    public static void main(String[] args) {
        int temp;
        IntStream.rangeClosed(1, 5).forEach(i -> {
            temp = i;
            System.out.println("value of i is = " + i);
        });
    }
}

Variable used in lambda expression should be final or effectively final

Since Lambda Expression is using the Local variable, it cannot be modified outside the Lambda Expression as well.

package org.wesome.java8;

import java.util.stream.IntStream;

class Apple {
    public static void main(String[] args) {
        int temp = 1;
        IntStream.rangeClosed(1, 5).forEach(i -> {
            System.out.println("value of temp is = " + temp);
        });
        temp = 2;
    }
}

local variables referenced from a lambda expression must be final or effectively final

Java Language Specification answers the reason for the above exception

Any local variable, formal parameter, or exception parameter used but not declared in a lambda expression must either be declared final or be effectively final (§4.12.4), or a compile-time error occurs where the use is attempted.

What is Final and Effective Final Variable

Final Variable: a variable which is declared with the final keyword cannot be modified and will have the same value throughout the application.

Effective Final Variable: a variable which is not explicitly declared with a final keyword but never gets modified throughout the application, although it's not declared as final since the value has not been modified hence compiler treats them just like final and can be termed as Effective Final.

if the Local Variable is just used but never get updated inside the lambda expression hence no need to declare it as final.
so the question arises, why does Lambda Expression need a variable to be a final or effective final

The restriction to effectively final variables prohibits access to dynamically-changing local variables, whose capture would likely introduce concurrency problems.

Concurrency

Streams allow Lambda Functions to execute in sequential and parallel as well. if the Lambda Expression will update the local variable defined outside, it might result in an incorrect value. local values are saved in stack, each thread has its own call stack, and no thread can access other thread stack

package org.wesome.java8;

import java.util.stream.IntStream;

class Apple {
    public static void main(String[] args) {
        int temp = 1;
        IntStream.rangeClosed(1, 5).parallel().forEach(i -> {
            temp = i;
        });
        System.out.println("value of temp variable is = " + temp);
    }
}

Why Lambda Requires Final or Effective Final

The main reason behind the requirement of final and Effective Final is Lambda Expressions can use local variables defined outside. compiler passes the variable in lambda using pass by value, lambda tries to keep the value as constant because changing the value of a variable with each iteration might result in an incorrect result.

The process of keeping local variables is known as capturing lambdas.

Lambda can capture static variables, instance variables, and local variables, but only local variables must be final or effectively final.

local variables are saved in the stack, each thread has its own call stack, and no thread can access another thread stack, where are static and instance variables are stored on the heap which is shared among all threads.

Then Why Lambda Allows local variable update if declared as one element array
Lambda expression doesn't allow updating of local variable if it's not declared as final but it does allow to update local variable if it's declared as one element array.

package org.wesome.java8;

import java.util.Arrays;
import java.util.stream.IntStream;

class Apple {
    public static void main(String[] args) {
        final int[] temp = new int[1];
        IntStream.rangeClosed(1, 5).parallel().forEach(i -> {
            temp[0] = i;
        });
        System.out.println("value of staticTemp variable is = " + Arrays.toString(temp));
    }
}

To understand this, we first have to understand how the final actually works.

when a variable is declared as final, it means its reference to the object is set to final.

final String apple = "Fuji";

int this case, reference apple has been updated with the memory location of string Fuji and will never be allowed to be updated, but

final String[] apple = {"Fuji"};

the array reference variable apple has to be updated with the memory location of the start of the array, but the array contains the address of the element Fuji, which is not declared final and hence can be changed.

So the array reference variable having memory location of Array is constant hence cannot be updated, but values inside the array can be updated.

Instance and static Variable in Lambda Expression

Java allows the instance variable and static variables to be used and modified as well inside the lambda expression.

package org.wesome.java8;

import java.util.stream.IntStream;

class Apple {
    static int staticTemp;
    private int instanceTemp;

    public static void main(String[] args) {
        Apple apple = new Apple();
        IntStream.rangeClosed(1, 5).forEach(i -> {
            apple.instanceTemp = i;
            staticTemp = i;
        });
        System.out.println("value of instanceTemp variable is = " + apple.instanceTemp);
        System.out.println("value of staticTemp variable is = " + apple.staticTemp);
    }
}

like this article? read more

Discover and read more posts from we some
get started