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

  • 110

    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)
  • 3

    Sometimes you may want to have a lambda expression implementing more than one interface. This is mostly useful with marker interfaces (such as java.io.Serializable) since they don't add abstract methods.

    For example, you want to create a TreeSet with a custom Comparator and then serialize it and send it over the network. The trivial approach:

    TreeSet<Long> ts = new TreeSet<>((x, y) -> Long.compare(y, x));
    

    doesn't work since the lambda for the comparator does not implement Serializable. You can fix this by using intersection types and explicitly specifying that this lambda needs to be serializable:

    TreeSet<Long> ts = new TreeSet<>(
        (Comparator<Long> & Serializable) (x, y) -> Long.compare(y, x));
    

    If you're frequently using intersection types (for example, if you're using a framework such as Apache Spark where almost everything has to be serializable), you can create empty interfaces and use them in your code instead:

    public interface SerializableComparator extends Comparator<Long>, Serializable {}
    
    public class CustomTreeSet {
      public CustomTreeSet(SerializableComparator comparator) {}
    }
    

    This way you're guaranteed that the passed comparator will be serializable.

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