Java: Variables used in lambda should be final or effectively final

Java: Variables used in lambda should be final or effectively final

In Java, a lambda expression can reference a variable outside the expression itself. It can reference:

  1. local variable(should be final or effectively final)

  2. instance variable

  3. static variable

Reference a local variable

If a expression references a local variable, the local variable should be final or effectively final. For example:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
import java.util.ArrayList;
import java.util.List;

public class LambdaReference {
public static void main(String[] args) {
List<String> list = new ArrayList<>();
list.add("Orange");
list.add("Marshall");
list.add("Aroma");

// s is a local variable, which is *inside* a method.
String s = "Amplifier";

list.forEach(e -> System.out.println(e + " " + s));
}
}

In the above code, the lambda function references a local variable s, and the output of the code is:

1
2
3
Orange Amplifier
Marshall Amplifier
Aroma Amplifier

In this case, the variable s should be effectively final, which means that we cannot change its value. If we change its value from “Amplifier” to “Factory”, there would be an error:

lambda_reference

Therefore, a more clean and straightfoward way is to declare the variable s as a final variable:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
import java.util.ArrayList;
import java.util.List;

public class LambdaReference {
public static void main(String[] args) {
List<String> list = new ArrayList<>();
list.add("Orange");
list.add("Marshall");
list.add("Aroma");

// We declare s as a final variable.
final String s = "Amplifier";

list.forEach(e -> System.out.println(e + " " + s));
}
}

Another thing worth noting is that, a variable of reference type is considered effectively final if the reference does not change, even if the object itself is mutable and changes. For example:

1
2
3
4
// The variable "set" is effectively final, even its content has
// been changed.
TreeSet<Employee> set = new TreeSet<Employee>();
list.forEach( entry -> set.add(entry) );

Reference an instance variable

A lambda expression can also reference an instance variable:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
import java.util.ArrayList;
import java.util.List;

public class LambdaReference {
// s is an instance variable.
String s = "Amplifier";

public void foo() {
List<String> list = new ArrayList<>();
list.add("Orange");
list.add("Marshall");
list.add("Aroma");

list.forEach(e -> System.out.println(e + " " + s));
}

public static void main(String[] args) {
LambdaReference lr = new LambdaReference();
lr.foo();
}
}

In the above code, s is an instance variable, and the output of the code is:

1
2
3
Orange Amplifier
Marshall Amplifier
Aroma Amplifier

Note that s does not have to be final or effectively final, which means that we can change the value of s:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
import java.util.ArrayList;
import java.util.List;

public class LambdaReference {
// s is an instance variable.
String s = "Amplifier";

public void foo() {
List<String> list = new ArrayList<>();
list.add("Orange");
list.add("Marshall");
list.add("Aroma");

list.forEach(e -> System.out.println(e + " " + s));

System.out.println("----------------------------");

// We can change the value of s.
s = "Factory";
list.forEach(e -> System.out.println(e + " " + s));
}

public static void main(String[] args) {
LambdaReference lr = new LambdaReference();
lr.foo();
}
}

The output is:

1
2
3
4
5
6
7
Orange Amplifier
Marshall Amplifier
Aroma Amplifier
----------------------------
Orange Factory
Marshall Factory
Aroma Factory

Reference a static variable

Similar to instance variable, static variable can be referenced by instance variable, and it needs not to be final or effectively final:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
import java.util.ArrayList;
import java.util.List;

public class LambdaReference {
// s is a static variable.
static String s = "Amplifier";

public static void main(String[] args) {
List<String> list = new ArrayList<>();
list.add("Orange");
list.add("Marshall");
list.add("Aroma");

list.forEach(e -> System.out.println(e + " " + s));

System.out.println("----------------------------");

s = "Factory";
list.forEach(e -> System.out.println(e + " " + s));
}
}

The output of the above code is:

1
2
3
4
5
6
7
Orange Amplifier
Marshall Amplifier
Aroma Amplifier
----------------------------
Orange Factory
Marshall Factory
Aroma Factory