Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Java 8 catch 22 with lambda expressions and effectively final

Tags:

java

java-8

I'm playing with Java 8 and hit a basic scenario that illustrates a catch 22 where fixing one compile error causes another compile error. The scenario (which is just an example simplified from something more complex):

public static List<String> catch22(List<String> input) {
    List<String> result = null;
    if (input != null) {
      result = new ArrayList<>(input.size());
      input.forEach(e -> result.add(e)); // compile error here
    }

    return result;
}

I get a compile error:

Local variable result defined in an enclosing scope must be final or effectively final

If I change the first line to:

List<String> result;

I get a compile error on the last line:

The local variable result may not have been initialized

It seems like the only approach here is to pre-initialize my result to an ArrayList, which I don't want to do, or not use lambda expressions. Am I missing any other solution?

like image 717
Josh Stone Avatar asked Apr 01 '14 05:04

Josh Stone


2 Answers

Here, i summarized some general solutions for lambdas expressions modifying method local variables.

Lambdas Expressions are actually anonymous inner classes in a concise form. When we use them in local methods we should take several constraints into consideration:

  • lambdas expressions(anonymous inner class) cant modify local variables of surrounding method link here they must be final or in java 8 effectively final

  • Unlike instance variables, local variables do not get default values and if you want to use or return them you must initialize them first

When these two constraints collide (in case of defining a lambdas in a method where lambdas modifies local variable in surrounding method), we get into trouble

solutions:

solution 1: do not modify method local variable in lambdas or use instance variables instead

solution 2: do some tricks, for example copy local variable into another one and pass that to lambdas:

public static List<String> catch22(List<String> input) {
 List<String> result = null;
 if (input != null) {
   result = new ArrayList<>(input.size());
   List<String> effectivelyFinalResult = result;
   input.forEach(e -> effectivelyFinalResult.add(e)); 
 }
 return result;
}

or limit local variables scope so you dont get into trouble for not initializing them:

public static List<String> catch22(List<String> input) {
if (input != null) {
    List<String> result; // result gets its value in the lambdas so it is effectively final
    result = new ArrayList<>(input.size());
    input.forEach(e -> result.add(e));
    return result;
} else {
    return null; 
}
} 
like image 192
Mr.Q Avatar answered Sep 16 '22 15:09

Mr.Q


Push the declaration inside the block with input != null. Example:

public static List<String> catch22(List<String> input) {
    if (input != null) {
        List<String> result;
        result = new ArrayList<>(input.size());
        input.forEach(e -> result.add(e)); // compile error here
        return result;
    } else {
        return null; // or empty list or something that suits your app
    }
}
like image 20
Raul Guiu Avatar answered Sep 19 '22 15:09

Raul Guiu