Variable Used in Lambda Expression Should Be Final or Effectively Final

Variable used in lambda expression should be final or effectively final

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.

Why variable used in lambda expression should be final or effectively final

I'd like to preface this answer by saying what I show below is not actually how lambdas are implemented. The actual implementation involves java.lang.invoke.LambdaMetafactory if I'm not mistaken. My answer makes use of some inaccuracies to better demonstrate the point.


Let's say you have the following:

public static void main(String[] args) {
String foo = "Hello, World!";
Runnable r = () -> System.out.println(foo);
r.run();
}

Remember that a lambda expression is shorthand for declaring an implementation of a functional interface. The lambda body is the implementation of the single abstract method of said functional interface. At run-time an actual object is created. So the above results in an object whose class implements Runnable.

Now, the above lambda body references a local variable from the enclosing method. The instance created as a result of the lambda expression "captures" the value of that local variable. It's almost (but not really) like you have the following:

public static void main(String[] args) {
String foo = "Hello, World!";

final class GeneratedClass implements Runnable {

private final String generatedField;

private GeneratedClass(String generatedParam) {
generatedField = generatedParam;
}

@Override
public void run() {
System.out.println(generatedField);
}
}

Runnable r = new GeneratedClass(foo);
r.run();
}

And now it should be easier to see the problems with supporting concurrency here:

  1. Local variables are not considered "shared variables". This is stated in §17.4.1 of the Java Language Specification:

    Memory that can be shared between threads is called shared memory or heap memory.

    All instance fields, static fields, and array elements are stored in heap memory. In this chapter, we use the term variable to refer to both fields and array elements.

    Local variables (§14.4), formal method parameters (§8.4.1), and exception handler parameters (§14.20) are never shared between threads and are unaffected by the memory model.

    In other words, local variables are not covered by the concurrency rules of Java and cannot be shared between threads.

  2. At a source code level you only have access to the local variable. You don't see the generated field.

I suppose Java could be designed so that modifying the local variable inside the lambda body only writes to the generated field, and modifying the local variable outside the lambda body only writes to the local variable. But as you can probably imagine that'd be confusing and counterintuitive. You'd have two variables that appear to be one variable based on the source code. And what's worse those two variables can diverge in value.

The other option is to have no generated field. But consider the following:

public static void main(String[] args) {
String foo = "Hello, World!";
Runnable r = () -> {
foo = "Goodbye, World!"; // won't compile
System.out.println(foo);
}
new Thread(r).start();
System.out.println(foo);
}

What is supposed to happen here? If there is no generated field then the local variable is being modified by a second thread. But local variables cannot be shared between threads. Thus this approach is not possible, at least not without a likely non-trivial change to Java and the JVM.

So, as I understand it, the designers put in the rule that the local variable must be final or effectively final in this context in order to avoid concurrency problems and confusing developers with esoteric problems.

Java 8 variable should be final or effectively final issue

You could avoid the problem by using the lambda expression to return true or false if there are any names that are not null, and assign the result to your boolean.

Something like this:

boolean hasAnyWithNames = list.stream().anyMatch(c -> c.getName() != null);

The choice "bool" is not a good one for variable name by the way.

Edit:

  • Replaced Boolean with base type per comment.
  • Used anyMatch() instead of filter() count per comment
    Thanks

Java 8 stream variable used in lambda should be final or effectively final

References may only be made to (effectively) final variables from within a lambda.

The reference held by finalResponse in effectively final, because it never changes. Note that changing the reference means assigning a new value to it, eg

finalResponse = someOtherList;

Changing the state of the object referred to (eg adding items to the list referred to by finalResponse) is irrelevant to what the value held by the variable finalResponse, ie

finalResponse.add(something);

Does not change the variable finalResponse; it only changes the object to which finalResponse refers.

Variable used in Lambda should be final or effective final while calculating sum?

All external variables used within the anonymous inner class or Lambda need to be final or effectively final(a non-final variable that is never reassigned).

In your solution, you are trying to fix classical imperative solution with a functional one.

An idiomatic Java-8 approach would be to use Stream API:

map.values().stream()
.map(x -> x.get("Z"))
.reduce(0, Double::sum);

or utilize the specialized Stream for doubles:

map.values().stream()
.mapToDouble(x -> x.get("Z"))
.sum()

Remember to properly handle edge cases. This will explode if there is no value associated with the "Z" key.

Why does variables in lambdas have to be final or effectively final?

It is related to multi-thread programming.

Local variables in Java have until now been immune to race conditions
and visibility problems because they are accessible only to the thread
executing the method in which they are declared. But a lambda can be
passed from the thread that created it to a different thread, and that
immunity would therefore be lost if the lambda, evaluated by the
second thread, were given the ability to mutate local variables. - Source

Why is the Variable used in Lambda expression must be final or effectively final warning ignored for instance variables

We tend to forget that instanceCounter is actually this.instanceCounter, you are capturing this which is well, effectively final. As to why this is needed, the answer is obviously here

Why don't instance fields need to be final or effectively final to be used in lambda expressions?

Instance variables are stored in the heap space whereas local variables are stored in the stack space. Each thread maintains its own stack and hence the local variables are not shared across the threads. On the other hand, the heap space is shared by all threads and therefore, multiple threads can modify an instance variable. There are various mechanisms to make the data thread-safe and you can find many related discussions on this platform. Just for the sake of completeness, I've quoted below an excerpt from http://web.mit.edu/6.005/www/fa14/classes/18-thread-safety/

There are basically four ways to make variable access safe in
shared-memory concurrency:

  • Confinement. Don’t share the variable between threads. This idea is called confinement, and we’ll explore it today.
  • Immutability. Make the shared data immutable. We’ve talked a lot about immutability already, but there are some additional constraints
    for concurrent programming that we’ll talk about in this reading.
  • Threadsafe data type. Encapsulate the shared data in an existing threadsafe data type that does the coordination for you. We’ll talk
    about that today.
  • Synchronization. Use synchronization to keep the threads from accessing the variable at the same time. Synchronization is what you
    need to build your own threadsafe data type.

Variable used in lambda should be final or effectively final for string appending

I wrote a simple example program

import java.util.Arrays;
import java.util.List;
import java.util.stream.Collectors;

public class Main {
public static void main(String[] args) {
List<Test> list = Arrays.asList(new Test(), new Test(), new Test());

String myStr = list.stream().map(Test::getMessage)
.collect(Collectors.joining(",", "optional prefix", "optional suffix"));
System.out.println(myStr); // output = optional prefixmessage,message,messageoptional suffix

}
}

class Test {
public String getMessage() {
return "message";
}
}

EDIT
If you have a List of Strings you could also do

List<String> stringList = Arrays.asList("a", "b", "c");
System.out.println(String.join(",", stringList));// a,b,c


Related Topics



Leave a reply



Submit