data-driven-docs

Selft training repo


Project maintained by ggranados Hosted on GitHub Pages — Theme by mattgraham

Lambda Expression


Table of Contents


Overview

Before Java 8, whenever you wanted to instantiate, for example, a new Runnable, you had to write an anonymous inner class like so:

Runnable runnable = new Runnable(){
    @Override
    public void run(){
        System.out.println("Hello world !");
    }
};

With lambdas, the same code looks like this:

Runnable runnable = () -> System.out.println("Hello world two!");

Anatomy of Lambda Expression 1


Examples

  1. Lambda Expression Syntax
  2. Lambda Listener
  3. Lambda Capture
  4. Lamdbda Inference

Lambda Expression Syntax

Overall, the code aims to illustrate how lambda expressions can simplify the implementation of functional interfaces and make the code more readable and concise.

The code defines a Comparator interface for comparing Employee objects based on their names. It starts with a traditional anonymous class implementation and then converts it into lambda expressions in several steps.

    // old fashion anonymous class implementation
    Comparator<Employee> byName = new Comparator<Employee>() {
        @Override
        public int compare(Employee a, Employee b) {
            return a.getName().compareTo(b.getName());
        }
    };

    // First lambda expression
    Comparator<Employee> byNameLambda1 =
    (Employee a, Employee b) -> {return a.getName().compareTo(b.getName()); };

    // Removing parameter types
    Comparator<Employee> byNameLambda2 =
    (a,b) -> { return a.getName().compareTo(b.getName()); };

    // Removing braces and return
    Comparator<Employee> byNameLambda3 =
    (a,b) -> a.getName().compareTo(b.getName());

The code also demonstrates the usage of lambda expressions with the Runnable interface. It creates multiple Thread instances using lambda expressions to define the behavior of the thread.

    Thread t1 = new Thread(r);

    // No need to even mention Runnable
    Thread t2 = new Thread(() -> {
        System.out.println("An implicit Runnable");
    });
    
    // No need for braces here
    Thread t3 = new Thread(() -> System.out.println("An implicit Runnable!"));

Lastly, the code showcases a lambda expression with a single parameter using the Consumer functional interface. It defines a Consumer that takes a String and prints its length.

    // Expression with one parameter
    Consumer<String> lengthPrinter =
            s -> System.out.println(s.length());

View on Github | Back to top


Lambda Listener

This Java code demonstrates the usage of anonymous classes and lambda expressions in the context of Swing GUI programming.

Creates a simple Swing GUI window with two text fields and two buttons. Clicking the “Say Hello” button updates the text fields with “Hello, world!” using an anonymous class.

    // For this we use a regular anonymous class
    helloButton.addActionListener(new ActionListener() {
        @Override
        public void actionPerformed(ActionEvent e) {
            staticTextField.setText("Hello, world!");
            localTextField.setText("Hello, world");
        }
    });

Clicking the “Say Goodbye” button updates the text fields with “Goodbye, world!” using a lambda expression.

    // For this we use a lambda expression (actually, block)
    JButton goodbyeButton = new JButton("Say Goodbye");
    goodbyeButton.addActionListener( event -> {
        staticTextField.setText("Goodbye, world!");
        localTextField.setText("Goodbye, world");
    });

View on Github | Back to top


Lambda Capture

By capturing variables from their surrounding scope, lambda expressions provide convenient access to those variables without explicitly passing them as parameters. The type of capturing (non-capturing, instance-capturing, or constant-capturing) determines which variables are accessible within the lambda expression.

Non-capturing Lambda Expression: A non-capturing lambda expression does not capture (or access) any variables from its surrounding scope. It directly uses the parameters or constants defined within the lambda expression itself.

    // Non-capturing lambda, one instance
    System.out.println("\nNon-capturing lambda:");
    for (int i=0; i<5; i++) {
        Consumer<String> myPrinter2 =
                msg -> System.out.println("Consuming " + msg);

        myPrinter2.accept(myPrinter2.toString());
    }

Instance-Capturing Lambda Expression: An instance-capturing lambda expression captures (or accesses) instance variables from the enclosing class. These variables belong to the object on which the lambda expression is invoked.

    // Constant-capturing lambda, one instance
    System.out.println("\nConstant-capturing lambda:");
    final int secret = 42;
    for (int i = 0; i < 5; i++) {
        Consumer<String> myPrinter3 =
                msg -> System.out.println("Consuming " + msg + ", " + secret);

        myPrinter3.accept(myPrinter3.toString());
		}

Constant-Capturing Lambda Expression: A constant-capturing lambda expression captures (or accesses) final or effectively final variables from the surrounding scope. These variables are treated as constants because their values do not change after they are assigned.

    private int id = 1;
    public void foo() {
        System.out.println("\nInstance-capturing lambda:");
    
        for (int i=0; i<5; i++) {
            // this-capturing lambda, many instances!
            Consumer<String> myPrinter4 =
                    msg -> System.out.println("Consuming " + msg + ", " + id);
    
            myPrinter4.accept(myPrinter4.toString());
        }
    }

View on Github | Back to top


Lambda Inference

This Java code demonstrates the inference rules for lambda expressions and shows different scenarios related to type inference.

a. The first lambda expression Consumer<String> c1 = msg -> System.out.println(msg.length()); showcases the standard syntax for lambda expressions. The type of the lambda expression parameter (msg) is inferred based on the expected type of the Consumer interface, which is String in this case.

    // Standard syntax
    Consumer<String> c1 = msg -> System.out.println(msg.length());

b. The second and third examples (Object x1 and Object x2) intentionally cause compile-time errors. They show situations where there is not enough information available for the compiler to infer the type of the lambda expression parameter. In these cases, explicit type information is needed to resolve the error.

    // Compile-time error: not enough info
    Object x1 = msg -> System.out.println(msg.length());

    //Compile-time error: not enough info
    Object x2 = (String msg) -> System.out.println(msg.length());

c. The fourth example Object x3 = (Consumer<String>)((String msg) -> System.out.println(msg.length())); resolves the compile-time error by explicitly casting the lambda expression to the Consumer<String> type. By providing the explicit cast, the compiler can determine the type of the lambda expression parameter.

    // Ok: cast added
    Object x3 = (Consumer<String>)((String msg) -> System.out.println(msg.length()));

d. The fifth and sixth examples demonstrate scenarios where the inferred type is Object due to the use of wildcard (?). In the case of Consumer<?> c2 = msg -> System.out.println(msg); the inferred type is Object because the wildcard does not provide specific type information.

    //OK: but inferred type is Object
    Consumer<?> c2 = msg -> System.out.println(msg);

The same applies to Consumer<?> c4 = (String msg) -> System.out.println(msg.length()); where the explicit manifest type (String) is added to the parameter.

    // Ok: added manifest type to parameter
    Consumer<?> c4 = (String msg) -> System.out.println(msg.length());

e. The commented line Consumer<?> c3 = msg -> System.out.println(msg.length()); causes a compile-time error. It demonstrates that type inference is not based on the body of the lambda expression. Even though the body of the lambda expression uses msg.length(), the type inference does not consider it when determining the type of the lambda expression parameter.

    //Compile-time error: Inference is *not* based on body of lambda
    Consumer<?> c3 = msg -> System.out.println(msg.length());

View on Github | Back to top


Ref.


Get Started | Languages | Java Development | Java 8 | PF in Java


  1. https://lifebeginsatfortyblog.wordpress.com/2016/07/01/the-java-tutorials-lambda-expressions/