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.

inline side-by-side expand all collapse all

Examples

  • 34

    Lambda expressions provide a clear and concise way of representing a method interface using an expression.

    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.

    Optionally, the @FunctionalInterface annotation can be used so that a compiler error will be generated if the interface is not a functional interface.


    Examples of functional 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();
    }
    @FunctionalInterface
    interface Foo5 {
        void bar();
    }
    @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 method:

    interface BadFoo {
        void bar();
        void quux(); // <-- Second method prevents lambda: which one do we use?
    }
    

    The body of a lambda is used as the body of the functional interface's single method. Thus, these two examples are equivalent:

    Foo1 longFoo = new Foo1() {
        @Override
        public void bar() {
            System.out.println("foo");
        }
    };
    Foo1 shortFoo = () -> System.out.println("foo");
    
    // Both of these print "foo":
    longFoo.bar();
    shortFoo.bar();
    

    Because a lambda is simply 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);
    passMeALambda(() -> System.out.println("Lambda called"));
    

    Java 8 provides some functional interfaces out of the box, which can be found in the java.util.function package. Learning these will provide in-depth understanding on functional interfaces

  • 37

    Prior to Java 8, the java.util.Comparator interface was implemented as shown below, in order to sort a collection:

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

    Starting with Java 8, this anonymous inner class can be replaced with a lambda expression:

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

    The type information Person can be removed to simplify the declaration:

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

    This can be simplified further with the use of Comparator.comparing:

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

    Using a static import can improve the readability and conciseness:

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

    Comparators built this way can also be easily chained together, that is, if we want to compare persons by their first name, and in case of equality, then compare by last name :

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

    Currying is the technique of translating the evaluation of a function that takes multiple arguments into evaluating a sequence of functions, each with a single argument.

    This is normally useful when for example:

    1. Different arguments of a function are calculated at different times. (see Example 1)
    2. Different arguments of a function are calculated by different tiers of the application. (see Example 2)

    Here's a generic utility that applies currying on a 2-argument function:

    class FunctionUtils {
    
        public static <A,B,C> Function<A,Function<B,C>> curry(BiFunction<A, B, C> f) {
            return a -> b -> f.apply(a,b);
        }
    
    }
    

    The above returned curried lambda expression can also be viewed/written as:

    a -> ( b -> f.apply(a,b) );
    

    Example 1

    Let's assume that the total yearly income is a function composed by the income and a bonus:

    BiFunction<Integer,Integer,Integer> totalYearlyIncome = (income,bonus) -> income + bonus;
    

    Let's assume that the yearly income portion is known in advance:

    Function<Integer,Integer> partialTotalYearlyIncome = FunctionUtils.curry(totalYearlyIncome).apply(10000);
    

    And at some point down the line the bonus is known:

    partialTotalYearlyIncome.apply(100);
    

    Example 2

    Let's assume that the car manufacturing involves the application of car wheels and car body:

    BiFunction<String,String,String> carManufacturing = (wheels,body) -> wheels.concat(body);
    

    These parts are applied by different factories:

    class CarWheelsFactory {
        public Function<String,String> applyCarWheels(BiFunction<String,String,String> carManufacturing) {
            return FunctionUtils.curry(carManufacturing).apply("applied wheels..");
        }
    }
    
    class CarBodyFactory {
        public String applyCarBody(Function<String,String> partialCarWithWheels) {
            return partialCarWithWheels.apply("applied car body..");
        }
    }
    

    Notice that the CarWheelsFactory above curries the car manufacturing function and only applies the wheels. The car manufacturing process then will take the below form:

    CarWheelsFactory carWheelsFactory = new CarWheelsFactory();
    CarBodyFactory   carBodyFactory   = new CarBodyFactory();
    
    BiFunction<String,String,String> carManufacturing = (wheels,body) -> wheels.concat(body);
    
    Function<String,String> partialCarWheelsApplied = carWheelsFactory.applyCarWheels(carManufacturing);
    String carCompleted = carBodyFactory.applyCarBody(partialCarWheelsApplied);
    

I am downvoting this example because it is...

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

Lambda expressions are relatively new (since Java SE 8 - released 2014-03-18), and as such, may not have been fully optimized. Depending on the number of lambdas used, the operations performed, and various other factors, lambdas may be slightly slower than traditional methods [source]. However, they are usually faster than anonymous classes, and may even perform as fast, or even faster than alternate solutions.

Also, keep in mind that lambda expressions can only be used in conjunction with functional interfaces.

Still have question about Lambda Expressions? Ask Question

Introduction to Java lambdas

34

Lambda expressions provide a clear and concise way of representing a method interface using an expression.

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.

Optionally, the @FunctionalInterface annotation can be used so that a compiler error will be generated if the interface is not a functional interface.


Examples of functional 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();
}
@FunctionalInterface
interface Foo5 {
    void bar();
}
@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 method:

interface BadFoo {
    void bar();
    void quux(); // <-- Second method prevents lambda: which one do we use?
}

The body of a lambda is used as the body of the functional interface's single method. Thus, these two examples are equivalent:

Foo1 longFoo = new Foo1() {
    @Override
    public void bar() {
        System.out.println("foo");
    }
};
Foo1 shortFoo = () -> System.out.println("foo");

// Both of these print "foo":
longFoo.bar();
shortFoo.bar();

Because a lambda is simply 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);
passMeALambda(() -> System.out.println("Lambda called"));

Java 8 provides some functional interfaces out of the box, which can be found in the java.util.function package. Learning these will provide in-depth understanding on functional interfaces

Declare a comparator

37

Prior to Java 8, the java.util.Comparator interface was implemented as shown below, in order to sort a collection:

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

Starting with Java 8, this anonymous inner class can be replaced with a lambda expression:

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

The type information Person can be removed to simplify the declaration:

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

This can be simplified further with the use of Comparator.comparing:

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

Using a static import can improve the readability and conciseness:

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

Comparators built this way can also be easily chained together, that is, if we want to compare persons by their first name, and in case of equality, then compare by last name :

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

Currying

6

Currying is the technique of translating the evaluation of a function that takes multiple arguments into evaluating a sequence of functions, each with a single argument.

This is normally useful when for example:

  1. Different arguments of a function are calculated at different times. (see Example 1)
  2. Different arguments of a function are calculated by different tiers of the application. (see Example 2)

Here's a generic utility that applies currying on a 2-argument function:

class FunctionUtils {

    public static <A,B,C> Function<A,Function<B,C>> curry(BiFunction<A, B, C> f) {
        return a -> b -> f.apply(a,b);
    }

}

The above returned curried lambda expression can also be viewed/written as:

a -> ( b -> f.apply(a,b) );

Example 1

Let's assume that the total yearly income is a function composed by the income and a bonus:

BiFunction<Integer,Integer,Integer> totalYearlyIncome = (income,bonus) -> income + bonus;

Let's assume that the yearly income portion is known in advance:

Function<Integer,Integer> partialTotalYearlyIncome = FunctionUtils.curry(totalYearlyIncome).apply(10000);

And at some point down the line the bonus is known:

partialTotalYearlyIncome.apply(100);

Example 2

Let's assume that the car manufacturing involves the application of car wheels and car body:

BiFunction<String,String,String> carManufacturing = (wheels,body) -> wheels.concat(body);

These parts are applied by different factories:

class CarWheelsFactory {
    public Function<String,String> applyCarWheels(BiFunction<String,String,String> carManufacturing) {
        return FunctionUtils.curry(carManufacturing).apply("applied wheels..");
    }
}

class CarBodyFactory {
    public String applyCarBody(Function<String,String> partialCarWithWheels) {
        return partialCarWithWheels.apply("applied car body..");
    }
}

Notice that the CarWheelsFactory above curries the car manufacturing function and only applies the wheels. The car manufacturing process then will take the below form:

CarWheelsFactory carWheelsFactory = new CarWheelsFactory();
CarBodyFactory   carBodyFactory   = new CarBodyFactory();

BiFunction<String,String,String> carManufacturing = (wheels,body) -> wheels.concat(body);

Function<String,String> partialCarWheelsApplied = carWheelsFactory.applyCarWheels(carManufacturing);
String carCompleted = carBodyFactory.applyCarBody(partialCarWheelsApplied);

Method References

6

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.

List<String> strings = getStrings();

Instance method reference (to an arbitrary instance)

strings.stream().map(String::toString)

The equivalent lambda

strings.stream().map((s) -> s.toString())

Here, a method reference to the instance method toString() of type String, 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)

strings.foreach(System.out::println);

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

The equivalent lambda:

strings.foreach((s) -> System.out.println(s));

Static method reference

strings.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:

 strings.stream().map((s) -> String.valueOf(s))

Reference to a constructor

strings.stream().map(Integer::new)

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));

Iterating through a HashMap using a BiConsumer

2

HashMap has the forEach method that accepts a BiConsumer. BiConsumer is a functional interface which contains one method, public void accept(T t, U u), where T and U are parameterized types.

The following examples are for printing out the Key-Value pairs for a HashMap<String,String>; however, the concept still applies elsewhere.


Assume that a HashMap<String, String> is stored in variable hm, and that java.util.HashMap is imported for all of the following examples.

Without a lambda expression:

public class StringBiConsumer implements BiConsumer<String, String> {
    public void apply(String str1, String str2) {
        //Your code here
        System.out.println(str1 + ": " + str2);
    }
}

hm.forEach(new StringBiConsumer());

We can improve this a little bit without using lambda expressions by writing an anonymous class:

hm.forEach(new BiConsumer<String, String>() {
    public void apply(String str1, String str2) {
        System.out.println(str1 + ": " + str2);
    }
});

A bit more concise, but still bulky. We can do better.


Since functional interfaces (such as BiConsumer) only contain one unimplemented method, the designers of java guessed that with these types of interfaces, you probably only want to override just that one method. When you write a lambda expression, the "method" that the lambda expression represents automatically overrides the single unimplemented method of the lambda target (in this example, the lambda target is BiConsumer), which must be a functional interface. With that in mind, let's rewrite this as a lambda expression:

hm.forEach((String str1, String str2)->{ 
    //The -> operator tells java that you are writing a lambda expression.
    //Note that the parameter types are the same as a BiConsumer<String, String>
    System.out.println(str1+ ": " + str2);
    //If the return type isn't void, you need a return statement
});

Java can also inference the parameter types (since HashMap<String, String> only accepts a BiConsumer<? super String, ? super String>, java infers that you are creating a BiConsumer<String, String>). Let's see this in action:

hm.forEach((str1, str2)->{
    System.out.println(str1 + ": " + str2);
});

Since this isn't a particularly complicated statement, this can be shortened even further:

hm.forEach((str1, str2)->System.out.println(str1 + ": " + str2));

Alternatively, you can reuse the lambda expression:

BiConsumer<String, String> exampleConsumer= (str1, str2)->System.out.println(str1 + ": " + str2);
hm.forEach(exampleConsumer);
someOtherHm.forEach(exampleConsumer);

If you have a method elsewhere that matches the parameter and return types of the lambda target, you can use a method reference. This code is valid:

public class ExampleClass {
    
    public static void someRandomMethod(String str1, String str2) {
        System.out.println(str1 + ": " str2);
    }

    public static void processMap(HashMap<String, String> hm) {
        hm.forEach(ExampleClass::someRandomMethod);
    }
}

This code is equivalent to:

hm.forEach((String s1, String s2)->ExampleClass.someRandomMethod(s1, s2));

This can also extend to non-static methods, as long as the parameter and return types still match:

hm.forEach(anObject::anotherMethod);

One final note: for all of the examples where the code doesn't directly mention BiConsumer by name, there is no need to import java.util.function.BiConsumer.

`return` only returns from the lambda, not the outer method

1

The return method only returns from the lambda, not the outer method.

Beware that this is different from Scala and Kotlin!

void threeTimes(IntConsumer r) {
  for (int i = 0; i < 3; i++) {
    r.accept(i);
  }
}

void demo() {
  threeTimes(i -> {
    System.out.println(i);
    return; // Return from lambda to threeTimes only!
  });
}

This can lead to unexpected behavior when attempting to write own language constructs, as in builtin constructs such as for loops return behaves differently:

void demo2() {
  for (int i = 0; i < 3; i++) {
    System.out.println(i);
    return; // Return from 'demo2' entirely
  }
}

In Scala and Kotlin, demo and demo2 would both only print 0. But this is not more consistent. The Java approach is consistent with refactoring and the use of classes - the return in the code at the top, and the code below behaves the same:

void demo3() {
  threeTimes(new MyIntConsumer());
}

class MyIntConsumer implements IntConsumer {
  public void accept(int i) {
    System.out.println(i);
    return;
  }
}

Therefore, the Java return is more consistent with class methods and refactoring, but less with the for and while builtins, these remain special.

Because of this, the following two are equivalent in Java:

IntStream.range(1, 4)
    .map(x -> x * x)
    .forEach(System.out::println);
IntStream.range(1, 4)
    .map(x -> { return x * x; })
    .forEach(System.out::println);

Furthermore, the use of try-with-resources is safe in Java:

class Resource implements AutoCloseable {
  public void close() { System.out.println("close()"); }
}

void executeAround(Consumer<Resource> f) {
  try (Resource r = new Resource()) {
    System.out.print("before ");
    f.accept(r);
    System.out.print("after ");
  }
}

void demo4() {
  executeAround(r -> {
    System.out.print("accept() ");
    return; // Does not return from demo4, but frees the resource.
  });
}

will print before accept() after close(). In the Scala and Kotlin semantics, the try-with-resources would not be closed, but it would print before accept() only.

Implementing multiple interfaces

1

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.

Introduction To Java Closures

1

A closure uses variables that are outside of the function scope. This is not a problem in traditional procedural programming – you just use the variable – but when you start producing functions at runtime it does become a problem.

make_fun() is creating and returning a function called func_to_return, which is then used by the rest of the program.

 public class LambdaExample {
    
    public Function<Integer, Integer> make_fun() {
        int n = 0;
        return arg -> {
            System.out.print(n + " " + arg + ": "); // here 'n' is binded to the outside 'n'
            // can't perform: n += 1;
            arg += 1;
            return n + arg;
        };
    }

    public void try_it() {
        Function<Integer, Integer>
            x = make_fun(),
        for(int i = 0; i < 5; i++)
            System.out.println(x.apply(i));
    }

    public static void main(String[] args) {
        new LambdaExample().try_it();
    }

}

Note that n in the scope of make_fun is binded to n inside the returned function but the reference is "effectively final", meaning that in the returned function it is not allowed to change n's value, for example, n += 1;

To overcome this, it is possible to implement a class containing the variable int n and change its value using set:

 class myInt {
    private int n = 0;
    public myInt(int n){this.n = n;}
    public void setN(int n){this.n = n;}
    public int getN(){return this.n;}
}

And in the above example:

 public Function<Integer, Integer> make_fun() {
        myInt n = new myInt(0);
        return arg -> {
            System.out.print(n.getN() + " " + arg + ": "); 
            arg += 1;
            n.setN(n.getN() + 1);
            return n.getN() + arg;
        };
    }

Lambda - Listener Example

1

Anonymous class listener

JButton btn = new JButton("My Button");
btn.addActionListener(new ActionListener() {
    @Override
    public void actionPerformed(ActionEvent e) {
        System.out.println("Button was pressed");
    }
});

Lambda listener

JButton btn = new JButton("My Button");
btn.addActionListener(e -> {
    System.out.println("Button was pressed");
});

Lambdas and Execute-around Pattern

1

There are several good examples of using lambdas as a FunctionalInterface in simple scenarios. A fairly common use case that can be improved by lambdas is what is called the Execute-Around pattern. In this pattern, you have a set of standard setup/teardown code that is needed for multiple scenarios surrounding use case specific code. A few common example of this are file io, database io, try/catch blocks.

interface DataProcessor {
    void process( Connection connection ) throws SQLException;;
}

public void doProcessing( DataProcessor processor ) throws SQLException{
    try (Connection connection = DBUtil.getDatabaseConnection();) {
        processor.process(connection);
        connection.commit();
    } 
}

Then to call this method with a lambda it might look like:

public static void updateMyDAO(MyVO vo) throws DatabaseException {
    doProcessing((Connection conn) -> MyDAO.update(conn, ObjectMapper.map(vo)));
}

This is not limited to I/O operations. It can apply to any scenario where similar setup/tear down tasks are applicable with minor variations. The main benefit of this Pattern is code re-use and enforcing DRY (Don't Repeat Yourself).

Using lambda expression with your own functional interface

1

Lambdas are meant to provide inline implementation code for single method interfaces and the ability to pass them around as we have been doing with normal variables. We call them Functional Interface.

For example, writing a Runnable in anonymous class and starting a Thread looks like:

//Old way
new Thread(
        new Runnable(){
            public void run(){
                System.out.println("run logic...");
            }
        }
).start();

//lambdas, from Java 8
new Thread(
        ()-> System.out.println("run logic...")
).start();

Now, in line with above, lets say you have some custom interface:

interface TwoArgInterface {
    int operate(int a, int b);
}

How do you use lambda to give implementation of this interface in your code? Same as Runnable example shown above. See the driver program below:

public class CustomLambda {
    public static void main(String[] args) {

        TwoArgInterface plusOperation = (a, b) -> a + b;
        TwoArgInterface divideOperation = (a,b)->{
            if (b==0) throw new IllegalArgumentException("Divisor can not be 0");
            return a/b;
        };

        System.out.println("Plus operation of 3 and 5 is: " + plusOperation.operate(3, 5));
        System.out.println("Divide operation 50 by 25 is: " + divideOperation.operate(50, 25));

    }
}

Topic Outline