Java 8 — Local Variables and Lambdas

Lambda and Local Variables

@FunctionalInterface
interface PrintCoin{
void print();
}

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

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();
}
}
Hey Gurl! I see dem coins. I got 100 coins

Local variable initialized in a method

@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();
}
}
Hey Gurl! I see dem coins. I got 100 coins

Local variable initialized in 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();
}
}
java: local variables referenced from a lambda expression must be final or effectively final

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

@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();
}
}
java: local variables referenced from a lambda expression must be final or effectively final

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.

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();
}
}
Printing coins that are not consumed by lambda = 101
Hey Gurl! I dont want no coins

Manipulating Local Variables inside Lambda Functions

@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();
}
}
java: local variables referenced from a lambda expression must be final or effectively final

Lambda functions consuming local reference variable

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();
}
}
java: local variables referenced from a lambda expression must be final or 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();
}
}
200 Doge coins in my bag!

Declaring Variables Inside Lambda Function

@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();
}
}
200 Doge coins in my bag!
@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();
}
}
201 Doge coins in my bag!
202 Doge coins ++
@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();
}
}
Doge coins bought = [80, 20]
Doge coins bought = [100, 200]

This in Lambda

@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();
}
}
Doge coins bought = 30
@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();
}
}
coin = 20
this.coin = 30

Naming Convention in Lambda

@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();
}
}
java: variable coin is already defined in method getDoge()
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();
}
}
java: variable coin is already defined in method getDoge()
Pineapples flexing after learning Local Variables in Lambdas

--

--

Get the Medium app

A button that says 'Download on the App Store', and if clicked it will lead you to the iOS App store
A button that says 'Get it on, Google Play', and if clicked it will lead you to the Google Play store