Variable Used in Lambda Expression Should Be Final or Effectively Final

Variable used in lambda expression should be final or effectively final

When I try to use calTz it is showing this error.

private TimeZone extractCalendarTimeZoneComponent(Calendar cal, TimeZone calTz) {
    try {
        cal.getComponents().getComponents("VTIMEZONE").forEach(component -> {
            VTimeZone v = (VTimeZone) component;
            v.getTimeZoneId();
            if (calTz == null) {
                calTz = TimeZone.getTimeZone(v.getTimeZoneId().getValue());
            }
        });
    } catch (Exception e) {
        log.warn("Unable to determine ical timezone", e);
    }
    return null;
}
6

9 Answers

Although other answers prove the requirement, they don't explain why the requirement exists.

The JLS mentions why in §15.27.2:

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

To lower risk of bugs, they decided to ensure captured variables are never mutated.

6

A final variable means that it can be instantiated only one time. in Java you can't reassign non-final local variables in lambda as well as in anonymous inner classes.

You can refactor your code with the old for-each loop:

private TimeZone extractCalendarTimeZoneComponent(Calendar cal,TimeZone calTz) {
    try {
        for(Component component : cal.getComponents().getComponents("VTIMEZONE")) {
        VTimeZone v = (VTimeZone) component;
           v.getTimeZoneId();
           if(calTz==null) {
               calTz = TimeZone.getTimeZone(v.getTimeZoneId().getValue());
           }
        }
    } catch (Exception e) {
        log.warn("Unable to determine ical timezone", e);
    }
    return null;
}

Even if I don't get the sense of some pieces of this code:

  • you call a v.getTimeZoneId(); without using its return value
  • with the assignment calTz = TimeZone.getTimeZone(v.getTimeZoneId().getValue()); you don't modify the originally passed calTz and you don't use it in this method
  • You always return null, why don't you set void as return type?

Hope also these tips helps you to improve.

1

From a lambda, you can't get a reference to anything that isn't final. You need to declare a final wrapper from outside the lamda to hold your variable.

I've added the final 'reference' object as this wrapper.

private TimeZone extractCalendarTimeZoneComponent(Calendar cal,TimeZone calTz) {
    final AtomicReference<TimeZone> reference = new AtomicReference<>();

    try {
       cal.getComponents().getComponents("VTIMEZONE").forEach(component->{
        VTimeZone v = (VTimeZone) component;
           v.getTimeZoneId();
           if(reference.get()==null) {
               reference.set(TimeZone.getTimeZone(v.getTimeZoneId().getValue()));
           }
           });
    } catch (Exception e) {
        //log.warn("Unable to determine ical timezone", e);
    }
    return reference.get();
}   
5

Java 8 has a new concept called “Effectively final” variable. It means that a non-final local variable whose value never changes after initialization is called “Effectively Final”.

This concept was introduced because prior to Java 8, we could not use a non-final local variable in an anonymous class. If you wanna have access to a local variable in anonymous class, you have to make it final.

When lambda was introduced, this restriction was eased. Hence to the need to make local variable final if it’s not changed once it is initialized as lambda in itself is nothing but an anonymous class.

Java 8 realized the pain of declaring local variable final every time a developer used lambda, introduced this concept, and made it unnecessary to make local variables final. So if you see the rule for anonymous classes has not changed, it’s just you don’t have to write the final keyword every time when using lambdas.

I found a good explanation here

2

In your example, you can replace the forEach with lamdba with a simple for loop and modify any variable freely. Or, probably, refactor your code so that you don't need to modify any variables. However, I'll explain for completeness what does the error mean and how to work around it.

Java 8 Language Specification, §15.27.2:

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.

Basically you cannot modify a local variable (calTz in this case) from within a lambda (or a local/anonymous class). To achieve that in Java, you have to use a mutable object and modify it (via a final variable) from the lambda. One example of a mutable object here would be an array of one element:

private TimeZone extractCalendarTimeZoneComponent(Calendar cal, TimeZone calTz) {
    TimeZone[] result = { null };
    try {
        cal.getComponents().getComponents("VTIMEZONE").forEach(component -> {
            ...
            result[0] = ...;
            ...
        }
    } catch (Exception e) {
        log.warn("Unable to determine ical timezone", e);
    }
    return result[0];
}
1

A variable used in lambda expression should be a final or effectively final, but you can assign a value to a final one element array.

private TimeZone extractCalendarTimeZoneComponent(Calendar cal, TimeZone calTz) {
    try {
        TimeZone calTzLocal[] = new TimeZone[1];
        calTzLocal[0] = calTz;
        cal.getComponents().get("VTIMEZONE").forEach(component -> {
            TimeZone v = component;
            v.getTimeZoneId();
            if (calTzLocal[0] == null) {
                calTzLocal[0] = TimeZone.getTimeZone(v.getTimeZoneId().getValue());
            }
        });
    } catch (Exception e) {
        log.warn("Unable to determine ical timezone", e);
    }
    return null;
}
1

if it is not necessary to modify the variable than a general workaround for this kind of problem would be to extract the part of code which use lambda and use final keyword on method-parameter.

to answer to > Variable used in lambda expression should be final or effectively final JAVA

to workaround that in not an elegant way , 2 issues : the side effect and the threading issue

final AtomicInteger e = new AtomicInteger(0);
        new Thread(() -> {
            e.addAndGet(1);
        });

to be more precise, i agree is kind of the same but the idea behind using Lambda function is to avoid side affect, and when we are accessing this final reference in the lambda function to populate the value to get the result from outside, we are breaking this concept.

in the oldest post you might want to rewrite like that

cal.getComponents().getComponents("VTIMEZONE").streams().map(v->v.getTimeZoneId().getValue()).collect(Collectors.toList());

and for the threading aspect , we have the same issue with the side effect and additionally you will never know when to access to the Atomic variable to collect the result , you could put a CountDownLatch ... Better to work with CompletableFuture to handle the result and the synchronization aspect

3

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.

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.

Check this Variable used in Lambda Expression Should be Final or Effectively Final

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Maya Lin-Takahashi

Maya Lin-Takahashi

Consumer Tech & Gadget Reviewer

Maya is a hardware enthusiast who tests and reviews smart home devices, smartphones, wearables, and audio gear. She focuses on practical consumer value and build quality.

Share this article
Twitter Facebook Pinterest