Dev Learning Lab

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

Fundamentals › Java

Lambdas

What is a lambda a shorter way to write?

A lambda is short code written with an arrow (->). () -> System.out.println("Working") is a short way to write "an object that implements an interface with only one method." Here we use a Task interface that has just one method, run, and the code after the arrow is what run does. Written out in full, the same thing is code that creates a class that implements Task and creates an object with new. In this lab you write the lambda out as an anonymous class and as a class with a name and see that the result is the same, and you compile and run the same code to see whether the code after the arrow runs on the line where the lambda is written, what happens if you remove t.run() or call it twice, and what happens if Task has one more method.

Checked against real execution · Eighteen combinations were compiled with javax.tools on JDK 21, run, and checked

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).
Anonymous class
This is a class that is given no name and from which one object is created right where the class is declared. You write its body in braces after the name of an interface or a class, as in new Runnable() { ... }, and the result is an object that implements that interface or extends that class. Its advantage is that you do not have to create a separate file for a small implementation that is used only once. Inside an anonymous class, this refers to that newly created object, which is different from a lambda expression, where this refers to the outer object. When the code is compiled, a separate class file is produced whose name is the name of the outer class followed by a number, such as Outer$1.
Interface
This is a type that defines the list of methods (the contract) that a class must implement. It cannot have instance variables, and one class can implement several of them. If the calling code depends only on this type, it is easy to change the implementing class or to replace it with a fake implementation for testing.

Try these

Running with the default settings

  1. You compile with javac. The compiler reads the source from the top and comes to the interface Task declaration. Task has just one method that is written with only a name and no body: run. Whoever implements Task has to write only run.
  2. The compiler reads the line Task t = () -> System.out.println("Working"); It reads the code after the arrow as what the method that Task must implement does, and Task has just one method to implement, run, so that code is settled to be what run does. You do not need to write the name separately. Compiling is not running, so the code after the arrow does not run.
  3. Compilation succeeds and the Main class file to run is created.
  4. You run it with java Main. main starts and the line Task t = () -> System.out.println("Working"); runs. This line creates "an object that runs the code after the arrow when run is called" and stores it in the variable t. The code after the arrow does not run on this line, so "Working" was not printed. All the line that writes the lambda does is store what to do in t.
  5. System.out.println("Ready") runs and prints Ready. This is the only thing printed so far. The object is already stored in t, but run has not been called even once.
  6. t.run() runs. The run of the object stored in t is called, and at that moment the code after the arrow, System.out.println("Working"), runs. "Working" is printed, and the number of times run has run becomes 1. "Working" came after "Ready." The code ran at the moment it was called, not where it was written.
  7. main ends. The output, in order, is Ready, Working.

Result of this run

Code

interface Task {
    void run();
}

public class Main {
    public static void main(String[] args) {
        // Lambda: stores what run does. It does not run on this line
        Task t = () -> System.out.println("Working");
        System.out.println("Ready");
        t.run();
    }
}

Assumptions of this model

The compiler and runtime are JDK 21.0.12.1 (javac 21). For all eighteen combinations, the source that is the same as the code on the screen (identical text, line by line) was compiled with javax.tools, and for those that compiled, the class file was loaded with a new class loader and Main.main was run, and the results were checked against the standard output that was captured. What was checked is whether compilation succeeded, the diagnostic code of the first error (the "Task is not a functional interface, multiple non-overriding abstract methods found in interface Task" of compiler.err.prob.found.req was restated as "Task has more than one method, so it cannot be used as a lambda," and the "does not override abstract method stop() in Task" of compiler.err.does.not.override.abstract was restated as "The stop method of Task was not implemented"), the number of errors and the line where each error occurred, whether the Main class file was created, and the output when run. The facts that the step sentences rely on were checked by running separate source in the same way. Right after the line that stores the object, t is an object of a class that implements Task in all three forms, and "Working" was not printed on that line (the lambda is an object of a hidden class that is created on this JDK, and the anonymous class and the class with a name, Work, were each an object of the class with exactly that name). The documentation was checked by opening the Java Language Specification (Java SE 21) directly. That a lambda expression has a functional interface as its target type in assignment, invocation, and casting contexts is from Section 15.27.3; that evaluating a lambda expression at run time creates a reference to an object of a class that implements the target functional interface, which is different from executing the body of the lambda, and that the body of the lambda is executed when a method of that interface is invoked, is from the introduction of Section 15.27 and Section 15.27.4; that a functional interface is an interface that has just one abstract method (not counting the methods of Object), and that an object of it can also be created with a lambda expression, is from Section 9.8; and that an anonymous class is declared implicitly by a class instance creation expression and can have an interface as its direct superinterface is from Section 15.9.5. Only part of each of those sections came back as a summary, so I could not read the whole original text. The kind of error, the number of errors, and the line where it occurs are not defined by the specification; they were observed on this javac. This lab covers only "a lambda is a short way to write an object that implements an interface with only one method" and when it runs. The object that a lambda creates is not exactly the same as the object of an anonymous class. On this JDK it is created as a hidden class, and it is said to differ in other ways too, such as what this refers to, but this lab did not check that. Lambdas with parameters, lambdas that return a value, method references, built-in functional interfaces such as Runnable and Comparator, and interfaces with default methods or static methods are not covered, and they are covered in the in-depth lab "Lambda expressions and functional interfaces".

Previous lab: List and Map · Next lab: Streams

Concepts in this lab

What this lab shows

Settings

Code


      
    

Diagram

Run comparison

Try these