Java Language


Lambda Expressions Java SE 8–Java SE 9 (Early Access)

Java SE 1.0
Java SE 1.1
Java SE 1.2
Java SE 1.3
Java SE 1.4
Java SE 5
Java SE 6
Java SE 7
Java SE 8
Java SE 9 (Early Access)

This draft deletes the entire topic.

Introduction

Lambda expressions provide a clear and concise way of implementing a single-method interface using an expression. It allows you to reduce the amount of code you have to create and maintain. It's often a concise replacement for anonymous classes.
expand all collapse all

Examples

  • 108

    Functional Interfaces

    Lambdas can only operate on a functional interface, which is an interface with just one abstract method. Functional interfaces can have any number of default or static methods. (For this reason, they are sometimes refered to as Single Abstract Method Interfaces, or SAM Interfaces).

    interface Foo1 {
        void bar();
    }
    
    interface Foo2 {
        int bar(boolean baz);
    }
    
    interface Foo3 {
        String bar(Object baz, int mink);
    }
    
    interface Foo4 {
        default String bar() { // default so not counted
            return "baz";
        }
        void quux();
    }
    

    When declaring a functional interface the @FunctionalInterface annotation can be added. This has no special effect, but a compiler error will be generated if this annotation is applied to an interface which is not functional, thus acting as a reminder that the interface should not be changed.

    @FunctionalInterface
    interface Foo5 {
        void bar();
    }
    
    @FunctionalInterface
    interface BlankFoo1 extends Foo3 { // inherits abstract method from Foo3
    }
    
    @FunctionalInterface
    interface Foo6 {
        void bar();
        boolean equals(Object obj); // overrides one of Object's method so not counted
    }
    

    Conversely, this is not a functional interface, as it has more than one abstract method:

    interface BadFoo {
        void bar();
        void quux(); // <-- Second method prevents lambda: which one should be considered as lambda?
    }
    

    This is also not a functional interface, as it does not have any methods:

    interface BlankFoo2 { }
    

    Java 8 also provides a number of generic templated functional interfaces in the package java.util.function. For example, the built-in interface Predicate<T> wraps a single method which inputs a value of type T and returns a boolean.


    Lambda Expressions

    The basic structure of a Lambda expression is:

    FunctionalInterface fi = () -> System.out.println("Hello");

    fi will then hold a singleton instance of an anonymous class which implements FunctionalInterface and where the one method's definition is { System.out.println("Hello"); }. In other words, the above is mostly equivalent to:

    FunctionalInterface fi = new FunctionalInterface() {
        @Override
        public void theOneMethod() {
            System.out.println("Hello");
        }
    };
    

    You cannot specify the name of the method when using a lambda—but you shouldn't need to, because a functional interface must have only one abstract method, so Java overrides that one.

    In cases where the type of the lambda is not certain, (e.g. overloaded methods) you can add a cast to the lambda to tell the compiler what its type should be, like so:

    Object fooHolder = (Foo1) () -> System.out.println("Hello");
    System.out.println(fooHolder instanceof Foo1); // returns true
    

    If the functional interface's single method takes parameters, the local formal names of these should appear between the brackets of the lambda. There is no need to declare the type of the parameter or return as these are taken from the interface (although it is not an error to declare the parameter types if you want to). Thus, these two examples are equivalent:

    Foo2 longFoo = new Foo2() {
        @Override
        public int bar(boolean baz) {
            return baz ? 1 : 0;
        }
    };
    Foo2 shortFoo = (x) -> { return x ? 1 : 0; };
    

    The parentheses around the argument can be omitted if the function only has one argument:

    Foo2 np = x -> { return x ? 1 : 0; }; // okay
    Foo3 np2 = x, y -> x.toString() + y // not okay
    

    Implicit Returns

    If the code placed inside a lambda is a Java expression rather than a statement, it is treated as a method which returns the value of the expression. Thus, the following two are equivalent:

    IntUnaryOperator addOneShort = (x) -> (x + 1);
    IntUnaryOperator addOneLong = (x) -> { return (x + 1); };
    

    Accessing Local Variables (value closures)

    Since lambdas are syntactic shorthand for anonymous classes, they follow the same rules for accessing local variables in the enclosing scope; the variables must be treated as final and not modified inside the lambda.

    IntUnaryOperator makeAdder(int amount) {
        return (x) -> (x + amount); // Legal even though amount will go out of scope
                                    // because amount is not modified
    }
    
    IntUnaryOperator makeAccumulator(int value) {
        return (x) -> { value += x; return value; }; // Will not compile
    }
    

    If it is necessary to wrap a changing variable in this way, a regular object that keeps a copy of the variable should be used. Read more in Java Closures with lambda expressions.


    Accepting Lambdas

    Because a lambda is an implementation of an interface, nothing special needs to be done to make a method accept a lambda: any function which takes a functional interface can also accept a lambda.

    public void passMeALambda(Foo1 f) {
        f.bar();
    }
    passMeALambda(() -> System.out.println("Lambda called"));
    
  • 63

    Prior to Java 8, it was necessary to implement the java.util.Comparator interface with an anonymous (or named) class when sorting a collection:

    Java SE 1.2
    Collections.sort(
        personList,
        new Comparator<Person>() {
            public int compare(Person p1, Person p2){
                return p1.getFirstName().compareTo(p2.getFirstName());
            }
        }
    );
    

    Starting with Java 8, the anonymous class can be replaced with a lambda expression. Note that the types for the parameters p1 and p2 can be left out, as the compiler will infer them automatically:

    Collections.sort(
        personList, 
        (p1, p2) -> p1.getFirstName().compareTo(p2.getFirstName())
    );
    

    The example can be simplified by using Comparator.comparing and method references expressed using the :: (double colon) symbol.

    Collections.sort(
        personList,
        Comparator.comparing(Person::getFirstName)
    );
    

    A static import allows us to express this more concisely, but it is debatable whether this improves overall readability:

    import static java.util.Collections.sort;
    import static java.util.Comparator.comparing;
    //...
    sort(personList, comparing(Person::getFirstName));
    

    Comparators built this way can also be chained together. For example, after comparing people by their first name, if there are people with the same first name, the thenComparing method with also compare by last name:

    sort(personList, comparing(Person::getFirstName).thenComparing(Person::getLastName));
    
  • 19

    Method references allow predefined static or instance methods that adhere to a compatible functional interface to be passed as arguments instead of an anonymous lambda expression.

    Assume that we have a model:

    class Person {
        private final String name;
        private final String surname;
    
        public Person(String name, String surname){
            this.name = name;
            this.surname = surname;
        }
    
        public String getName(){ return name; }
        public String getSurname(){ return surname; }
    }
    
    List<Person> people = getSomePeople();
    

    Instance method reference (to an arbitrary instance)

    people.stream().map(Person::getName)
    

    The equivalent lambda:

    people.stream().map(person -> person.getName())
    

    In this example, a method reference to the instance method getName() of type Person, is being passed. Since it's known to be of the collection type, the method on the instance (known later) will be invoked.


    Instance method reference (to a specific instance)

    people.forEach(System.out::println);
    

    Since System.out is an instance of PrintStream, a method reference to this specific instance is being passed as an argument.

    The equivalent lambda:

    people.forEach(person -> System.out.println(person));
    

    Static method reference

    Also for transforming streams we can apply references to static methods:

    List<Integer> numbers = Arrays.asList(1, 2, 3, 4, 5, 6);
    numbers.stream().map(String::valueOf)
    

    This example passes a reference to the static valueOf() method on the String type. Therefore, the instance object in the collection is passed as an argument to valueOf().

    The equivalent lambda:

     numbers.stream().map(num -> String.valueOf(num))
    

    Reference to a constructor

    List<String> strings = Arrays.asList("1", "2", "3");
    strings.stream().map(Integer::new)
    

    Read Collect Elements of a Stream into a Collection to see how to collect elements to collection.

    The single String argument constructor of the Integer type is being used here, to construct an integer given the string provided as the argument. In this case, as long as the string represents a number, the stream will be mapped to Integers. The equivalent lambda:

    strings.stream().map(s -> new Integer(s));
    

    Cheat-Sheet

    Method Reference FormatCodeEquivalent Lambda
    Static methodTypeName::method(args) -> TypeName.method(args)
    Non-static method (from instance)instance::method(args) -> instance.method(args)
    Non-static method (no instance)TypeName::method(instance, args) -> instance.method(args)
    ConstructorTypeName::new(args) -> new TypeName(args)
  • 2

    A lambda closure is created when a lambda expression references the variables of an enclosing scope (global or local). The rules for doing this are the same as those for inline methods and anonymous classes.

    Local variables from an enclosing scope that are used within a lambda have to be final. With Java 8 (the earliest version that supports lambdas), they don't need to be declared final in the outside context, but must be treated that way. For example:

    int n = 0; // With Java 8 there is no need to explicit final
    Runnable r = () -> { // Using lambda
        int i = n;
        // do something
    };
    

    This is legal as long as the value of the n variable is not changed. If you try to change the variable, inside or outside the lambda, you will get the following compilation error:

    "local variables referenced from a lambda expression must be final or effectively final".

    For example:

    int n = 0;
    Runnable r = () -> { // Using lambda
        int i = n;
        // do something
    };
    n++; // Will generate an error.
    

    If it is necessary to use a changing variable within a lambda, the normal approach is to declare a final copy of the variable and use the copy. For example

    int n = 0;
    final int k = n; // With Java 8 there is no need to explicit final
    Runnable r = () -> { // Using lambda
        int i = k;
        // do something
    };
    n++;      // Now will not generate an error
    r.run();  // Will run with i = 0 because k was 0 when the lambda was created
    

    Naturally, the body of the lambda does not see the changes to the original variable.

    Note that Java does not support true closures. A Java lambda cannot be created in a way that allows it to see changes in the environment in which it was instantiated. If you to implement a closure that observes or makes changes to its environment, you should simulate it using a regular class. For example:

    // Does not compile ...
    public IntUnaryOperator createAccumulator() {
        int value = 0;
        IntUnaryOperator accumulate = (x) -> { value += x; return value; };
        return accumulate;
    }
    

    The above will not compile for reasons discussed previously. We can work around the compilation error as follows:

    // Compiles, but is incorrect ...
    public class AccumulatorGenerator {
        private int value = 0;
    
        public IntUnaryOperator createAccumulator() {
            IntUnaryOperator accumulate = (x) -> { value += x; return value; };
            return accumulate;
        }
    }
    

    The problem is that the breaks the design contract for the IntUnaryOperator interface which states that instances should be functional and stateless. If such a closure is passed to built-in functions that accept functional objects, it is liable to cause crashes or erroneous behavior. Closures that encapsulate mutable state should be implemented as regular classes. For example.

    // Correct ...
    public class Accumulator {
       private int value = 0;
    
       public int accumulate(int x) {
          value += x;
          return value;
       }
    }
    
Please consider making a request to improve this example.

Syntax

  • () -> { return expression; } // Zero-arity with function body to return a value.
  • () -> expression // Shorthand for the above declaration; there is no semicolon for expressions.
  • () -> { function-body } // Side-effect in the lambda expression to perform operations.
  • parameterName -> expression // One-arity lambda expression. In lambda expressions with only one argument, the parenthesis can be removed.
  • (Type parameterName, Type secondParameterName, ...) -> expression // lambda evaluating an expression with parameters listed to the left
  • (parameterName, secondParameterName, ...) -> expression // Shorthand that removes the parameter types for the parameter names. Can only be used in contexts that can be inferred by the compiler where the given parameter list size matches one (and only one) of the size of the functional interfaces expected.

Parameters

Parameters

Remarks

Pitfalls

Keep in mind that lambda expressions can only be used in conjunction with functional interfaces.

Still have a question about Lambda Expressions? Ask Question

Topic Outline