Lambda Expression Variable Capturing with Examples

Last Updated : 13 Jul, 2026

Variable capturing allows a lambda expression to access variables declared outside its own body, such as local, instance, and static variables. This feature enables lambdas to use values from their enclosing scope while following specific rules to ensure consistent and predictable behavior.

  • Variable capturing helps lambdas work with data from the enclosing scope.
  • Instance and static variables can be accessed and modified inside a lambda.
  • Local variables must be final or effectively final.

Variable Capturing

Variable Capturing allows a lambda expression to access variables, methods, and fields from its enclosing scope. It enables lambdas to work with existing data without explicitly passing every value as a parameter.

  • It helps write cleaner and more concise code by allowing lambdas to use variables from the surrounding context.
  • Variable capturing is widely used in callbacks, event handling, collections, and multithreading, where captured local variables must be final or effectively final.

Types of Variables Captured in Lambdas

Lambda expressions can capture three types of variables from their enclosing scope:

1. Capturing Instance Variables

A lambda expression can directly access the instance variables of its enclosing object. Since instance variables belong to the object rather than the method, they can also be modified inside the lambda

Java
class GFG{
    
    private int number = 10;

    void display(){
        
        Runnable r = () -> {
            System.out.println("Instance variable: " + number);
        };
        r.run();
    }

    public static void main(String[] args){
        
        new GFG().display();
    }
}

Output
Instance variable: 10

Explanation: The lambda expression captures the instance variable number from the current object using this.number. It can modify it if not declared final.

2. Capturing Static Variables

Static variables belong to the class instead of individual objects. Lambda expressions can access and modify static variables without any restrictions related to variable capturing.

Java
class GFG{
    
    private static int counter = 5;

    void increment(){
        
        Runnable r = () -> {
            counter++;
            System.out.println("Counter: " + counter);
        };
        r.run();
    }

    public static void main(String[] args){
        
        new GFG().increment();
    }
}

Output
Counter: 6

Explanation: The lambda directly modifies the static variable counter, since it is shared across all instances of the class.

3. Capturing Local Variables

A lambda expression can capture local variables declared in the enclosing method. However, these variables must be final or effectively final, meaning their value cannot change after initialization.

Java
class GFG{
    
    void show(){
        
        int num = 20;

        Runnable r = () ->{
            System.out.println("Local variable captured: " + num);
        };

        r.run();
    }

    public static void main(String[] args){
        
        new GFG().show();
    }
}

Output
Local variable captured: 20

Explanation: The variable num is captured by the lambda. Since it is not modified anywhere after initialization, it is considered effectively final.

Why Must Local Variables Be Final or Effectively Final?

Unlike instance and static variables, local variables exist only while the method is executing. A lambda expression may continue to exist even after the enclosing method has finished execution.

To avoid inconsistent values and unexpected behavior, Java allows lambdas to capture only final or effectively final local variables.

Java
import java.util.*;

public class GFG{
    public static void main(String[] args){
        
        List<Runnable> tasks = new ArrayList<>();

        for (int i = 0; i < 3; i++){
            
            // Effectively final variable
            int temp = i; 
            tasks.add(
                () -> System.out.println("Value: " + temp));
        }

        tasks.forEach(Runnable::run);
    }
}

Output
Value: 0
Value: 1
Value: 2

Explanation: Each lambda captures a different copy of the variable temp (which is effectively final).
If we used i directly without creating a separate variable, it would cause a compile-time error because i changes in each loop iteration.

Comment