Concepts / Lambda Expressions

Lambda Expressions

A functional interface is a Java interface with a single abstract method, and Runnable is a standard example with one method, run.

  • Programming

Predicting Output Before the Trace

Start by reading a small program and predicting what it prints. The program below uses a lambda expression in a place where many Java programmers would otherwise write a named class. Look at the three lines first, and do not worry yet about the arrow symbol. Focus on the variable called greeting and the method call on the last line of the first statement's use, which is greeting.run().

java

What do you think happens?

What does this program print, and in what order?

  • Done only, because the lambda is only defined and never printed

  • Hi and then Done

  • Done and then Hi

Reveal answer
Answer:

Hi and then Done

The first statement creates a value of type Runnable from a lambda. Creating the lambda does not run its body. The line greeting.run() invokes the single method of the interface, and that call is what runs the body, printing Hi. The final line then prints Done.

What Makes an Interface Functional

Definition

A functional interface is a Java interface with exactly one abstract method. That single method defines the shape of a piece of behavior: what inputs it takes, what it returns, and what it is called. The interface itself does not say how the behavior is carried out. It only provides a template that some implementation must fill in.

Runnable is the standard example used to introduce the idea in its simplest form. Its declaration contains exactly one method, named run, which takes no parameters and returns nothing. Because the interface has a single method to implement, a lambda expression can stand in for a complete implementation of it. To identify a functional interface, look for the one abstract method. If you find that an interface has more than one abstract method, it does not match this definition, so a single lambda cannot supply all of its behavior.

Key point

The question to ask is how many abstract methods the interface declares. If the answer is one, the interface is functional, and its one method is the slot that a lambda fills.

Reading the Anatomy of a Lambda

A lambda expression supplies the body of the one method in a functional interface. In the hook example, the lambda is () -> System.out.println("Hi"). The empty parentheses on the left stand for the parameter list of run, which has no parameters. The arrow separates that parameter list from the body. The body on the right is the code that runs when run is called. The lambda therefore carries exactly the information the interface template leaves open: the implementation of the one method.

()parameter list of run,which is empty->separates parameters frombodySystem.out.println("Hi")body that runs when run iscalled
What are the parts of a lambda expression, and how does each part map onto the single abstract method run?
Runnabledeclares the method rungreetingvariable of type Runnablelambdasupplies the body of runtype of variablevalue held by variable
How does the interface declare the method, and how does the lambda supply its behavior through the variable greeting?

Tracing Where the Output Comes From

Tracing a program means following the order of execution and noting which line produces each piece of output. Consider the program below. It has the same shape as the hook, with one extra print statement placed between the creation of the lambda and the call to run.

Code walkthrough
Runnable greeting = () -> System.out.println("Hi");
System.out.println("Created");
greeting.run();
System.out.println("Finished");
  1. 1
    Line 1 the lambda is stored in greeting, whose type is Runnable. Nothing is printed.
  2. 2
    Line 2 the statement System.out.println("Created") runs and prints Created.
  3. 3
    Line 3 greeting.run() calls the one abstract method. The lambda body runs and prints Hi.
  4. 4
    Line 4 the final statement prints Finished.
  • Line 1 creates a Runnable value from the lambda. Its body is not executed here.
  • Line 2 prints Created directly.
  • Line 3 calls run on greeting, which hands control to the lambda body.
  • Line 4 prints Finished.
Output
Created
Hi
Finished
mainruns the statements inordergreetingRunnable valuelambda bodyprintln Hiconsoleshows printed text1. store lambda, nothing printed2. prints Created3. call run()hands control to body4. prints Hi5. prints Finished
When the program starts, which code runs first, which call hands control to the lambda body, and which line prints the output?
Worked example

Locating the source of the Hi line

In the program Created, Hi, Finished, which line of code is responsible for printing Hi, and why does the line that creates the lambda not print it?

  1. 1
    Identify the printed text

    The output has three lines. The middle line is Hi.

  2. 2
    Find the statement that prints Hi

    Hi appears only inside the lambda body, System.out.println("Hi").

  3. 3
    Find the statement that runs the body

    The body runs only when the single method run is invoked, which happens on the line greeting.run().

  4. 4
    Confirm the creation line is silent

    The creation line only stores the lambda in greeting. Storing the lambda does not invoke run, so nothing is printed there.

Result

Hi comes from the lambda body, and it is printed on the greeting.run() line, not on the line where the lambda is written.

Types as Variables and Return Types

The name of a functional interface is a type, in the same way that String and double are types. This means the name can declare variables, declare parameters, and specify what a method returns. Once a type is a functional interface, any value of that type can be created by writing a lambda expression instead of a conventional class. The type name is what connects the lambda to the method it implements.

java

In this program, the method makeTask has Runnable as its return type. The lambda it returns is a value of that type, so the caller can store it in a variable of type Runnable and invoke run on it, just as it would on any Runnable variable. Running this program prints Task done.

lambdabody prints Task donemakeTaskreturn type Runnabletvariable of type Runnablerun()prints Task donereturned as Runnableassigned to Runnable variablecaller invokes run
How does a lambda flow through a method whose return type is Runnable and into a variable of type Runnable?

A Lambda With a Parameter

The same pattern works for any interface you define yourself, as long as it has one abstract method. The interface below declares one method that takes a String and returns a String. Notice that the lambda's parameter list matches the parameter list of the method: it has one parameter, text, which corresponds to the String argument of apply.

java
Output
HELLO!
Notes on the code
  • Shout has exactly one abstract method, apply, so it is functional.
  • The lambda's parameter text matches the String parameter of apply.
  • The body returns a String, matching the return type of apply.

Mistakes When Tracing Lambda Programs

When a program using functional interfaces produces an unexpected result, the cause is usually one of a small number of mistakes. Each leaves a trace you can follow back to its source. The items below describe what the trace looks like and where to correct it.

  • Common mistake

    Expecting the lambda body to print as soon as the lambda is written

    What it looks like

    Runnable r = () -> System.out.println("Hi"); with nothing after it prints nothing.

    Why it fails

    Writing the lambda only creates a value of the functional interface type. Its body runs only when the single method is invoked.

    Do this instead

    Look for the call to run (or the interface's one method) and trace the program from that call.

  • Common mistake

    Assuming an interface is functional without counting its abstract methods

    What it looks like

    Treating an interface that declares two abstract methods as if one lambda could supply it.

    Why it fails

    A functional interface has exactly one abstract method. A lambda supplies the body of that one method only.

    Do this instead

    Count the abstract methods before writing a lambda for the interface type.

  • Common mistake

    Mismatching the lambda's parameters with the method's parameters

    What it looks like

    Writing () -> ... for an interface method that takes one String.

    Why it fails

    The lambda's parameter list must match the parameter list of the one method it implements.

    Do this instead

    Give the lambda one parameter for each parameter of the method, as in text -> text.toUpperCase().

Practice Tracing a Runnable Program

MEDIUM

Read the program below and predict its output line by line. For each printed line, name the statement that produced it: either the creation of a lambda, a call to run, or a plain print statement.

Runnable a = () -> System.out.println("one");
Runnable b = () -> System.out.println("two");
b.run();
a.run();
b.run();
Hints
  • Two lambdas are created on the first two lines, and neither prints anything at creation time.
  • Each printed line comes from a call to run, and the output follows the order of those calls.
  • The lambda stored in b prints two, and the lambda stored in a prints one.

What do you think happens?

What does the practice program print?

Reveal answer
Answer:

two, one, two

The first two lines only create lambdas. The three run calls then execute in order: b.run() prints two, a.run() prints one, and b.run() prints two again.

Key Takeaways

  • A functional interface is a Java interface with exactly one abstract method, and Runnable is a standard example whose single method is run.
  • A lambda expression supplies the body of that one method. Its empty or listed parameters match the method's parameters, and its body runs only when the method is invoked.
  • To trace a program, find the call that invokes the single method. The lambda body prints at that point, not at the point where the lambda is written.
  • The name of a functional interface is a type, so it can declare variables, parameters, and method return types, as in a method that returns Runnable.