Lambda ExpressionsS2C Home « Lambda Expressions

In the previous lesson we took an overview of Functional Interfaces which were introduced in Java8 and are interfaces containing a single abstract method; this does not include declaration of an abstract method overriding one of the public methods of java.lang.Object since any implementation of the interface will have an implementation from java.lang.Object or elsewhere.

But what do Functional Interfaces allow us to do? They allow us to use lambda expressions or method references to create a block of code that is available to process without executing it. This deferred execution allows us to pass the code as an argument to a method that will be executed later. This is a software development pattern known as behaviour parameterization and allows us to parameterize the method based on the passed block of code. We can derive a function descriptor from a Functional Interface that can be used via assignment of a lambda expression or method reference.

Function Descriptors Top

Lets take another look at the Predefined Functional Interfaces table with the derived function descriptor for each.

Functional Interface Function Descriptor
Within java.util.function package
BiConsumer<T,U>(T, U) -> void
BiFunction<T,U,R>(T, U) -> R
BinaryOperator<T>(T, T) -> T
BiPredicate<T,U>(T, U) -> boolean
Consumer<T>T -> void
Function<T,R>T -> R
Predicate<T>T -> boolean
Supplier<T>() -> T
UnaryOperator<T>T -> T
Existing interfaces that are functional in Java8
ActionListener(T, U) -> void
Callable() -> V
Comparator(E) -> void
Runnable() -> void

Lambda Expression Syntax Top

A lambda expression consists of 3 parts (argument-list) -> {body}

  1. Argument-list: Can be empty or non-empty.
  2. Arrow-token: Used to link argument-list and body of expression.
  3. Body: Expressions and statements for lambda expression.

Following are the various flavours of a lambda expression with 0, 1 and 2 parameters:



// NO PARAMETER LAMBDA  
() -> expression    
() -> {}    

// ONE PARAMETER LAMBDA  
// parameter -> expression
p -> expression    
// parameter -> code block
p -> {}    

// TWO PARAMETER LAMBDA  
// parameter1, parameter2 -> expression
(p1,p2) -> expression    
// parameter1, parameter2 -> code block
(p1,p2) -> {}    

Lets take a look at some code examples of lambda expressions starting with consumers.

Consumer and BiConsumer Examples Top

The Consumer functional interface represents an operation upon a single argument that produces no result whose functional method is accept(Object). Contrary to most other functional interfaces, Consumer is expected to operate via side-effects.

The BiConsumer functional interface represents an operation upon two arguments that produces no result whose functional method is accept(Object, Object). This is a two-arity specialization of the Consumer functional interface. Contrary to most other functional interfaces, BiConsumer is expected to operate via side-effects.

Following is a code sample using the Consumer and BiConsumer functional interfaces.


package com.server2client;

import java.util.Arrays;
import java.util.List;
import java.util.Objects;
import java.util.function.BiConsumer;
import java.util.function.Consumer;

public class ConsumerLambda {
    public static void main(String[] args) {
        List<String> numbersAsStringOne = Arrays.asList("one", "two", "three", "four", "five");
        List<String> numbersAsStringTwo = Arrays.asList("one", "two", "three", "four", "six");
        List<String> numbersAsStringThree = Arrays.asList("one", "two", "three", "four");
        List<String> numbersAsStringFour = Arrays.asList("one", "two", "three", "four", "five");

        // Consumer
        Consumer<String> consumerMethod = s -> System.out.println(s);
        numbersAsStringOne.forEach( consumerMethod );
        
        // BiConsumer
        BiConsumer<List<String>, List<String>> biConsumerMethod = (list1, list2) -> {
            if (list1.size() != list2.size()) {
                System.out.println("List sizes are not equal");
            }
            else {
                for (int i = 0; i < list1.size(); i++)
                    if (!Objects.equals(list1.get(i), list2.get(i))) {
                        System.out.println("List contents are not equal");
                        return;
                    }
                System.out.println("List contents are equal");
            }
        };
        biConsumerMethod.accept(numbersAsStringOne, numbersAsStringTwo);
        biConsumerMethod.accept(numbersAsStringOne, numbersAsStringThree);
        biConsumerMethod.accept(numbersAsStringOne, numbersAsStringFour);
    }
}

Save, compile and run the ConsumerLambda class in directory   c:\_OOConcepts in the usual way.

run ConsumerLambda class
Screenshot 1. Running the ConsumerLambda class.

The above screenshot shows the output of running our ConsumerLambda class.

Function and BiFunction Examples Top

The Function functional interface represents an operation that accepts a single argument and produces a result whose functional method is apply(Object).

The BiFunction functional interface represents an operation that accepts two arguments and produces a result whose functional method is apply(Object, Object). This is a two-arity specialization of the Function functional interface.

Following is a code sample using the Function and BiFunction functional interfaces.


package com.server2client;


import java.util.function.BiFunction;
import java.util.function.Function;

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

        // Function
        Function<Integer, Integer> functionMethod = a -> (a * a);
        System.out.println("Square: " + functionMethod.apply(5));

        // BiFunction
        BiFunction<Integer, Integer, Integer> biFunctionMethod = (a, b) -> (a * b);
        System.out.println("Product: " + biFunctionMethod.apply(7, 10));
    }
}

Save, compile and run the FunctionLambda class in directory   c:\_OOConcepts in the usual way.

run FunctionLambda class
Screenshot 2. Running the FunctionLambda class.

The above screenshot shows the output of running our FunctionLambda class.

UnaryOperator and BinaryOperator Examples Top

The UnaryOperator functional interface represents an operation upon a single operand, producing a same type result as the operand whose functional method is Function.apply(Object). This is a specialization of the Function functional interface for situations where the operand and result are of the same type.

The BinaryOperator functional interface represents an operation upon two same type operands, producing a same type result as the operands whose functional method is BiFunction.apply(Object, Object). This is a specialization of the BiFunction functional interface for situations where the operands and result are of the same type.

Following is a code sample using the UnaryOperator and BinaryOperator functional interfaces.


package com.server2client;


import java.util.function.BinaryOperator;
import java.util.function.UnaryOperator;

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

        // UnaryOperator
        UnaryOperator<Integer> unaryOperatorMethod = a -> (a * a);
        System.out.println("Square: " + unaryOperatorMethod.apply(15));

        // BinaryOperator
        BinaryOperator<Integer> binaryOperatorMethod = (a, b) -> (a * b);
        System.out.println("Product: " + binaryOperatorMethod.apply(77, 77));
    }
}

Save, compile and run the OperatorLambda class in directory   c:\_OOConcepts in the usual way.

run OperatorLambda class
Screenshot 3. Running the OperatorLambda class.

The above screenshot shows the output of running our OperatorLambda class.

Predicate and BiPredicate Examples Top

The Predicate functional interface represents a predicate (boolean-valued function) of one argument whose functional method is test(Object).

The BiPredicate functional interface represents a predicate (boolean-valued function) of two argumenta whose functional method is test(Object, Object). This is a two-arity specialization of the Predicate functional interface.

Following is a code sample using the Predicate and BiPredicate functional interfaces.


package com.server2client;


import java.util.function.BiPredicate;
import java.util.function.Predicate;

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

        // Predicate
        Predicate<String> predicate = (s1) ->
        {
            return s1.equals("two");
        };
        System.out.println(predicate.test("2"));
        System.out.println(predicate.test("two"));

        // BiPredicate
        BiPredicate<String, String> biPredicate = (s1, s2) ->
        {
            return s1.equals(s2);
        };
        System.out.println(biPredicate.test("2", "2"));
        System.out.println(biPredicate.test("2", "two"));
    }
}

Save, compile and run the PredicateLambda class in directory   c:\_OOConcepts in the usual way.

run PredicateLambda class
Screenshot 4. Running the PredicateLambda class.

The above screenshot shows the output of running our PredicateLambda class.

Supplier Example Top

The Supplier functional interface represents a supplier of non-specific results whose functional method is get().

Following is a code sample using the Supplier functional interface.


package com.server2client;


import java.util.function.Supplier;

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

        // Supplier
        Supplier<Double> supplier = () -> Math.random();
        System.out.println(supplier.get());
        System.out.println(supplier.get());
    }
}

Save, compile and run the SupplierLambda class in directory   c:\_OOConcepts in the usual way.

run SupplierLambda class
Screenshot 5. Running the SupplierLambda class.

The above screenshot shows the output of running our SupplierLambda class.

Functional From Java8 Top

With the advent of Java8 several well used interfaces were converted into functional interfaces, these being ActionListener, Callable, Comparator and Runnable. Using lambdas allows us to interact with these interfaces wihout the use of anonymous classes. This cuts down on code bloat and also makes the code much easier to read.

ActionListener Example Top

The ActionListener functional interface represents a listener interface for receiving action events whose functional method is actionPerformed(ActionEvent).

Following is some code highlighting usage of the ActionListener interface in Java8 and the old style using an anonymous class. As you can see the lambda expressions are much more concise and easy to read and the expression lambda even fits on one line!


package com.server2client;

import java.awt.*;
import java.awt.event.*;
import javax.swing.*;

public class ActionListenerLambda extends JFrame {

    public static void main(String[] args) {
        SwingUtilities.invokeLater(() -> new ActionListenerLambda().setVisible(true));
    }
    public ActionListenerLambda() {
        super("ActionListener Lambda Example");
        getContentPane().setLayout(new FlowLayout());
        JButton button = new JButton("Click Me!");
        getContentPane().add(button);

        // Anonymous Class 
        button.addActionListener(new ActionListener() {
            public void actionPerformed(ActionEvent evt) {
                System.out.println("Handled by Anonymous Class");
            }
        });

        // Expression Lambda
        button.addActionListener(e -> System.out.println("Handled by Expression Lambda"));

        // Statement Lambda
        button.addActionListener(e -> {
            System.out.println("Handled by Statement Lambda");
        });

        setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
        setSize(500, 100);
        setLocationRelativeTo(null);
    }
}

Save, compile and run the ActionListenerLambda class in directory   c:\_OOConcepts in the usual way.

run ActionListenerLambda Test
Screenshot 6. Running the ActionListenerLambda class.

The above screenshot shows the output of running our ActionListenerLambda class and clicking on the Click Me! button.

Callable Example Top

The Callable functional interface represents a task that returns a result and may throw an exception whose functional method is call().

Following is some code highlighting usage of the Callable interface in Java8 and the old style using an anonymous class. As you can see the lambda expression is much more concise and easy to read and even fits on one line!


package com.server2client;
  
import java.util.concurrent.Callable;

public class CallableLambda {

    public static void main(String[] args) {
        // Anonymous class example
        Callable<String> c1 = new Callable<String>() {
            @Override
            public String call() {
                return "Runnable with Anonymous Class";
            }
        };
        
        // Expression Lambda
        Callable<String> c2 = () -> "Runnable with Lambda Expression";

        try {
            System.out.println(c1.call());
        } catch (Exception e) {
            System.out.println(e.getMessage());
        }

        try {
            System.out.println(c2.call());
        } catch (Exception e) {
            System.out.println(e.getMessage());
        }
    }

}

Save, compile and run the CallableLambda class in directory   c:\_OOConcepts in the usual way.

run CallableLambda Test
Screenshot 7. Running the CallableLambda class.

The above screenshot shows the output of running our CallableLambda class.

Comparator Example Top

The Comparator functional interface represents a comparison function, which imposes a total ordering on some collection of objects.

Following is some code highlighting usage of the Comparator interface using an anonymous class prior to Java8 and using lambda epression from Java8 onwards. As you can see the expression lambda is much more concise and easy to read and even fits on one line!


package com.server2client;

import java.util.Comparator;
import static java.lang.Long.compare;

public class ComparatorLambda {

    public static void main(String[] args) {
        // Anonymous class example
        Comparator<Integer> c1 = new Comparator<Integer>() {
            @Override
            public int compare(Integer i1, Integer i2) {
                return Integer.compare(i1, i2);
            }
        };
        System.out.println(c1.compare(3, 5));
        System.out.println(c1.compare(5, 3));

        // Expression Lambda
        Comparator<Integer> c2 = (i1, i2) -> compare(i1, i2);
        System.out.println(c2.compare(3, 5));
        System.out.println(c2.compare(5, 3));
    }

}

Save, compile and run the ComparatorLambda class in directory   c:\_OOConcepts in the usual way.

run ComparatorLambda Test
Screenshot 8. Running the ComparatorLambda class.

The above screenshot shows the output of running our ComparatorLambda class.

Runnable Example Top

The Runnable functional interface represents a task that returns void and whose functional method is run(). The Runnable functional interface should be implemented by any class whose instances are intended to be executed by a thread.

Following is some code highlighting usage of the Runnable interface using an anonymous class prior to Java8 and using lambda epression from Java8 onwards. As you can see the lambda expressions are much more concise and easy to read and the expression lambda even fits on one line!


package com.server2client;
  
public class RunnableLambda {
    public static void main(String[] args) {
        // Anonymous class example
        Runnable r1 = new Runnable() {
            @Override
            public void run() {
                System.out.println("Runnable with Anonymous Class");
            }
        };
        // Statement Lambda
        Runnable r2 = () -> {
            System.out.println("Runnable with Lambda Statement");
        };
        // Expression Lambda
        Runnable r3 = () -> System.out.println("Runnable with Lambda Expression");

        new Thread(r1).start();
        new Thread(r2).start();
        new Thread(r3).start();
    }
}

Save and compile and run the RunnableLambda class in directory   c:\_OOConcepts in the usual way.

run RunnableLambda Test
Screenshot 9. Running the RunnableLambda class.

The above screenshot shows the output of running our RunnableLambda class.

Related Quiz

OO Concepts Quiz 14 - Lambda Expressions

Lesson 14 Complete

In this lesson we looked at lambda expressions which were introduced in Java8, their syntax and how to use them.

What's Next?

In the next lesson we learn about method references, which were also introduced in Java8, their syntax and how to use them.