Dev Learning Lab

The labs on this site need JavaScript to run. The same content is written out below.

Learn › Java › Lambdas and streams

Lambda expressions and functional interfaces

When does the code in a lambda expression run?

A lambda expression is syntax that lets you store "an action to run later" in a variable like a value and pass it to another method. The code inside a lambda expression does not run on the line where you write it. It runs only when the method that receives it calls it.

Checked against real execution · Checked by running 36 combinations on JDK 21

Concepts in this lab

Lambda expression
This is a syntax, introduced in Java 8, for writing a function with no name in a compact way. It has the form (parameters) -> body, and it is used as the implementation of a functional interface, which has only one abstract method. It lets you pass behavior (code) to another method as if it were a value.
Functional interface
This is an interface with only one abstract method (a redeclared method of Object such as equals is not counted, and any number of default and static methods is allowed), and it is a type to which a lambda expression or a method reference can be assigned. The java.util.function package predefines types such as Function (takes an input and returns a result), Supplier (supplies a value), Consumer (consumes a value), and Predicate (tests a condition).
Method reference
This is a syntax that refers to an existing method by name in place of a lambda. You write it by joining a class (or an object) and a method name with ::, as in String::length or System.out::println. It is a shorter way to write a lambda that does nothing but call one method.
Differences between anonymous classes and lambda expressions
An anonymous class is syntax that implements an interface or a class right where it is used and creates an object without giving the class a name, and it is written in the form new InterfaceName() { method implementations }. A lambda expression is syntax that does the same thing more briefly, in the form (parameters) -> statement, but only for a functional interface, which has exactly one abstract method. When you implement a functional interface with a simple body that does not use this, the result of a call is the same whichever of the two you use. However, an anonymous class can also implement an interface that has several methods, and it can have fields, and this inside it refers to the object of the anonymous class itself. this inside a lambda expression refers to the enclosing object in which the lambda expression is written.

Try these

Running with the default settings

  1. Function<String, Integer> f = s -> length(s): This creates an object that holds the action written to the right of the arrow token (->) and stores it in the variable f. The length method is not called yet. Nothing is printed.
  2. System.out.println("after creation"): "after creation" is printed. Execution has already passed the line that creates the action, but "body runs" has not been printed even once.
  3. run(f): The action held in the variable f is passed to the run method. An action is passed in the same way as a number or a string. Passing it does not make it run.
  4. System.out.println("run starts"): Execution enters run, and "run starts" is printed.
  5. f.apply("hello"): This is where the action finally runs. The length method is called, and "body runs: length(hello)" is printed. The returned value 5 is printed as "result 5".
  6. f.apply("hi"): The same action runs one more time. The length method is called, and "body runs: length(hi)" is printed. The returned value 2 is printed as "result 2".
  7. System.out.println("run ends"): "run ends" is printed. "body runs" was printed 2 times in all.

Result of this run

Code

class Demo {
    static int length(String s) {                    // what the action to pass does
        System.out.println("body runs: length(" + s + ")");
        return s.length();
    }

    static void run(Function<String, Integer> f) {      // the method that receives the action and calls it
        System.out.println("run starts");
        System.out.println("result " + f.apply("hello"));
        System.out.println("result " + f.apply("hi"));
        System.out.println("run ends");
    }

    public static void main(String[] args) {
        Function<String, Integer> f = s -> length(s);        // creates the action (it does not run yet)
        System.out.println("after creation");
        run(f);                                   // passes the action like a value
    }
}

Assumptions of this model

The output order, the number of times the body ran, and the returned values of the 36 combinations were checked against the result of running code with the same structure on JDK 21 on this PC. The name of the method that each interface calls, the number of values it takes, and the type it returns were also checked against values read from the real interfaces. Saying that an anonymous class, a lambda expression, and a method reference are the same means that the results seen in this lab (the output and the returned values) are the same. How the object is created internally and what this refers to differ among them. When a lambda expression uses a local variable of the enclosing scope, it can use only a variable whose value does not change, and this rule is explained in the "Inner classes and anonymous classes" lab.

Previous lab: Sorting and Comparator · Next lab: Lazy evaluation in streams

Concepts in this lab

What this lab shows

Settings

Code


      
    

Diagram

Run comparison

Try these