Java 8 — Local Variables and Lambdas

Sreenidhi KV
8 min readMay 17, 2021

Let’s break down how to consume local variable with lambda functions in java

The following scenarios will be covered,

  1. Lambda consumes local variable initialized in a method
  2. Local variable initialized in the Lambda function
  3. Manipulating local variable inside the lambda function
  4. Manipulating local variable outside the lambda function
  5. Lambda functions consuming local reference variable
  6. Declaring variables inside the lambda function
  7. Using This in Lambda
  8. Naming Convention in Lambda

Lambda and Local Variables

Let’s take the case where a variable is being consumed by Lambda

Let’s take a class (Doge) that consumes a Functional Interface (PrintCoin) which prints the number of coins.

The Functional Interface look like this, which takes a single abstract method print()

@FunctionalInterface
interface PrintCoin{
void print();
}

The Class Doge looks like this, we define the function as

() -> System.out.println("Hey Gurl! I see dem coins. I got "+coin+" coins");

which gets mapped to print()

class Doge{

public void getDoge(){
int coin = 100;
PrintCoin printCoin = () -> System.out.println("Hey Gurl! I see dem coins. I got "+coin+" coins");
printCoin.print();
}

public static void main(String []args){
Doge doge = new Doge();
doge.getDoge();
}
}

The output will look like this

Hey Gurl! I see dem coins. I got 100 coins

Super simple! The lambda just consumes the value.

Voila! It just gets super easy from here!

Local variable initialized in a method

Just like in the above example, the local variable coin is consumed by the lambda,

@FunctionalInterface
interface PrintCoin{
void print();
}

class Doge{

public void getDoge(){
int coin = 100; //local variable
PrintCoin printCoin = () -> System.out.println("Hey Gurl! I see dem coins. I got "+coin+" coins"); //lambda consumes it
printCoin.print();
}

public static void main(String []args){
Doge doge = new Doge();
doge.getDoge();
}
}

Output

Hey Gurl! I see dem coins. I got 100 coins

Local variable initialized in the Lambda function

Now let’s try and change the variable inside the lambda function

@FunctionalInterface
interface PrintCoin{
void print();
}

class Doge{

public void getDoge(){
int coin;
PrintCoin printCoin = () -> {
coin = 100; // Compile Time Error
System.out.println("Hey Gurl! I see dem coins. I got "+coin+" coins");
};
printCoin.print();
}

public static void main(String []args){
Doge doge = new Doge();
doge.getDoge();
}
}

Output

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

Whoa! What just happened?

Let’s break down all the questions scramming in our heads!

What is effectively final?

Without having to explicitly say that a variable is final, i.e unlike

final int number = 30;

where we have explicitly declared final. Incase of effectively final we won’t have to declare final keyword.

Final variable insinuates that it’s a constant and will never change. The same applies for effectively final. Except the compiler now can understand that it’s final without us having to explicitly write the final keyword it. Which means after initialization the value will never change.

Why do local variables need to be final or effectively final?

To ensure that no concurrency issues occur.

How can concurrency issues occur because of local variables?

Let’s say multiple threads are working on the method. Maintaining a consistent value for the variable gets difficult. The value of the variable is unpredicatble. Depending on the thread executing the value changes. So to deal with concurrent operations the local variables are final or effectively final.

Does this occur for static and instance variables too (Instance Variable, Static Variable)?

NOPE!

Why does it not occur for Static or Instance Variables?

Let’s talk about the memory storage of the variables,

Instance variables are stored in the heap area. Static variables previously were stored in PermGen (method area) and now in Metaspace. Local variables are stored on the stack.

What does this have got to do?

The stack reference is unique to each thread so the local variables are not common to each thread, whereas instance and static variables are commonly available and at all times their values will be consistent for each thread. In short, the location of where the variables are being placed is the reason why static and instance variables don’t face the issue.

Manipulating local variable outside the lambda function

Let’s have the lambda consume coin, but let’s manipulate the value outside the lambda as shown,

@FunctionalInterface
interface PrintCoin{
void print();
}

class Doge{

public void getDoge(){
int coin = 100;
PrintCoin printCoin = () -> {
System.out.println("Hey Gurl! I see dem coins. I got coins" + coin); //lambda consumes coin
};
coin ++; //manipulating outside lambda function
System.out.println(coin);
printCoin.print();
}

public static void main(String []args){
Doge doge = new Doge();
doge.getDoge();
}
}

Output?

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

Yuck! Again?

Why?

As stated above the local variables in lambdas are final or effectively final. Which ensures that the values consumed will always be consistent in a multithreaded environment.

Okay!

What if the lambda does not consume the variable?

Then it works like a normal local variable. As shown below.

@FunctionalInterface
interface PrintCoin{
void print();
}

class Doge{

public void getDoge(){
int coin = 100;
PrintCoin printCoin = () -> {
System.out.println("Hey Gurl! I dont want no coins");
};
coin ++; // not consumed by lambda
System.out.println("Printing coins that are not consumed by lambda = "+ coin);
printCoin.print();
}

public static void main(String []args){
Doge doge = new Doge();
doge.getDoge();
}
}

The output looks like this,

Printing coins that are not consumed by lambda = 101
Hey Gurl! I dont want no coins

Sweet!

Manipulating Local Variables inside Lambda Functions

Let’s manipulate the value of coin inside the lambda function now,

@FunctionalInterface
interface PrintCoin{
void print();
}

class Doge{

public void getDoge(){
int coin = 100;
PrintCoin printCoin = () -> {
coin ++; //manipulating inside lambda function
System.out.println("Hey Gurl! I dont want no coins");
};
printCoin.print();
}

public static void main(String []args){
Doge doge = new Doge();
doge.getDoge();
}
}

Output?

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

Why?

You know it! It’s the same reason all over! EFFECTIVELY FINAL/FINAL, CONCURRENCY Blah Blah!

Lambda functions consuming local reference variable

Final refers that once initialized we can’t reinitialize.

The same works for reference we can initialize it once but no reinitialization is allowed.

import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;

@FunctionalInterface
interface PrintCoin{
void print();
}

class Doge{

public void getDoge(){
int coin = 100;
List<Integer> coinsBought = new ArrayList();
PrintCoin printCoin = () -> {
coinsBought = Arrays.asList(80,20); //compile time error
System.out.println("Hey Gurl! I dont want no coins");
};
printCoin.print();
}

public static void main(String []args){
Doge doge = new Doge();
doge.getDoge();
}
}

Output is the same ole,

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

No reinitialization allowed for finals/effectively final!

How to manipulate the local variables of a method, that get’s consumed by Lambda function?

Can use Atomic References eg, AtomicIntegers which ensures concurrency. Although we shouldn’t be manipulating local variables inside lambdas.

Can we add values to references?

Don’t get confused!

Final ensures that the reference never changes, the value can change! As shown below.

import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;

@FunctionalInterface
interface PrintCoin{
void print();
}

class Doge{

public void getDoge(){
int coin = 100;
List<Integer> coinsBought = new ArrayList();
PrintCoin printCoin = () -> {
coinsBought.add(80); //allowed
coinsBought.add(20);

int value = coin + coinsBought.stream().reduce(0 , (coin1,coin2) -> coin1 + coin2);
System.out.println(value +" Doge coins in my bag!");
};
printCoin.print();
}

public static void main(String []args){
Doge doge = new Doge();
doge.getDoge();
}
}

The output looks like this,

200 Doge coins in my bag!

Yay!!

Declaring Variables Inside Lambda Function

Can we declare variables inside Lambda Function? HELL YEAH!

In the above example, we had done it too! Let’s dig down

@FunctionalInterface
interface PrintCoin{
void print();
}

class Doge{

public void getDoge(){
int coin = 100;
List<Integer> coinsBought = new ArrayList();
PrintCoin printCoin = () -> {
coinsBought.add(80);
coinsBought.add(20);
int value = coin + coinsBought.stream().reduce(0 , (coin1,coin2) -> coin1 + coin2); //declared a variable inside lambda
System.out.println(value +" Doge coins in my bag!");
};
printCoin.print();
}

public static void main(String []args){
Doge doge = new Doge();
doge.getDoge();
}
}

Output,

200 Doge coins in my bag!

Can we manipulate the local variable declared inside the lambda function? YES YES AND YES! It’s not final!

@FunctionalInterface
interface PrintCoin{
void print();
}

class Doge{

public void getDoge(){
int coin = 100;
List<Integer> coinsBought = new ArrayList();
PrintCoin printCoin = () -> {
coinsBought.add(80);
coinsBought.add(20);
int value = coin + coinsBought.stream().reduce(0 , (coin1,coin2) -> coin1 + coin2);
value ++;
System.out.println(value +" Doge coins in my bag!");
value ++; //can manipulate as it's not final!
System.out.println(value +" Doge coins ++");

};
printCoin.print();
}

public static void main(String []args){
Doge doge = new Doge();
doge.getDoge();
}
}

Output,

201 Doge coins in my bag!
202 Doge coins ++

Similarly, let’s see with references,

@FunctionalInterface
interface PrintCoin{
void print();
}

class Doge{

public void getDoge(){

PrintCoin printCoin = () -> {
List<Integer> coinsBought = new ArrayList();
coinsBought.add(80);
coinsBought.add(20);
System.out.println(" Doge coins bought = "+coinsBought);
coinsBought = Arrays.asList(100,200); //no error
System.out.println(" Doge coins bought = "+coinsBought);
};
printCoin.print();
}

public static void main(String []args){
Doge doge = new Doge();
doge.getDoge();
}
}

Output,

Doge coins bought = [80, 20]
Doge coins bought = [100, 200]

Worked!

This in Lambda

This refers to the current outer reference. Unlike Anonymous classes

@FunctionalInterface
interface PrintCoin{
void print();
}

class Doge{

int coin = 30; //this referes to current outer value
public void getDoge(){
int coin = 20;
PrintCoin printCoin = () -> {
System.out.println(" Doge coins bought = "+ this.coin);
};
printCoin.print();
}

public static void main(String []args){
Doge doge = new Doge();
doge.getDoge();
}
}

Output

Doge coins bought = 30

How to access the local variable? Remove this! Easy!

@FunctionalInterface
interface PrintCoin{
void print();
}

class Doge{

int coin = 30;
public void getDoge(){
int coin = 20;
PrintCoin printCoin = () -> {
System.out.println(" coin = "+ coin);
System.out.println(" this.coin = "+ this.coin);
};
printCoin.print();
}

public static void main(String []args){
Doge doge = new Doge();
doge.getDoge();
}
}

Output,

coin = 20
this.coin = 30

Naming Convention in Lambda

The local variable name defined inside the method and the local variable name defined inside the lambda function cannot be the same!

@FunctionalInterface
interface PrintCoin{
void print();
}

class Doge{

int coin = 30;
public void getDoge(){
int coin = 20;
PrintCoin printCoin = () -> {
int coin = 40; //cannot be the same
};
printCoin.print();
}

public static void main(String []args){
Doge doge = new Doge();
doge.getDoge();
}
}

Output

java: variable coin is already defined in method getDoge()

The local variable name defined inside the method and the parameter name of the lambda function cannot be the same!

package com.company;

import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;

@FunctionalInterface
interface PrintCoin{
void print(int value);
}

class Doge{

int coin = 30;
public void getDoge(){
int coin = 20;
PrintCoin printCoin = (coin) -> { //cannot define same name

};
printCoin.print(200);
}

public static void main(String []args){
Doge doge = new Doge();
doge.getDoge();
}
}

Output

java: variable coin is already defined in method getDoge()

You’ve aced Lambdas yay!

Pineapples flexing after learning Local Variables in Lambdas

That’s all folks!

Play around more! Do Let me know if I missed any scenarios.

Cheers!

--

--