[JAVA core technology] lambda expression and method reference

Lambda expressions, also known as closures, are the most important new feature that drives the release of Java 8.

Lambda allows functions to be used as parameters of a method (functions are passed into the method as parameters).

Using Lambda expressions can make the code more concise and compact.

grammar

The syntax format of lambda expression is as follows:

(parameters) -> expression
 or
(parameters) ->{ statements; }

Here are some examples of Lambda expressions:

(int a, int b) -> {  return a + b; }

() -> System.out.println("Hello World");

(String s) -> { System.out.println(s); }

() -> 42

() -> { return 3.1415 };

Structure of Lambda expression

Let's take a look at the structure of Lambda expressions.

A Lambda expression can have zero or more parameters

  • The type of parameter can be explicitly declared or inferred from the context. For example: (int a) has the same effect as (a)
  • All parameters shall be enclosed in parentheses and separated by commas. For example: (a, b) or (int a, int b) or (String a, int b, float c)
  • Empty parentheses indicate that the parameter set is empty. For example: () - > 42
  • Parentheses () can be omitted when there is only one parameter and its type can be deduced. For example: a - > return a * a
  • The body of a Lambda expression can contain zero or more statements
  • If the body of a Lambda expression has only one statement, curly braces {} can be omitted. The return type of anonymous function is consistent with the body expression
  • If the body of a Lambda expression contains more than one statement, the expression must be contained in curly braces {} (forming a code block). The return type of the anonymous function is the same as that of the code block. If there is no return, it is null

What is a functional interface

In Java, the marker type interface is an interface without method or attribute declaration. In short, the marker interface is an empty interface. Similarly, a functional interface is an interface that contains only one abstract method declaration.

java.lang.Runnable is a functional interface, in which only one method void run() is declared. Similarly, ActionListener interface is also a functional interface. We use anonymous inner classes to instantiate the objects of the functional interface. With Lambda expressions, this method can be simplified.

Each Lambda expression can be implicitly assigned to a functional interface. For example, we can create a reference to the Runnable interface through the Lambda expression.

Runnable r = () -> System.out.println("hello world");

When a functional interface is not specified, the compiler automatically interprets this conversion:

new Thread(
   () -> System.out.println("hello world")
).start();

Therefore, in the above code, the compiler will automatically infer that the Lambda expression is assigned to the Runnable interface according to the constructor signature public Thread(Runnable r) {} of the thread class.

Some expressions of Lambda interface are:

Consumer<Integer>  c = (int x) -> { System.out.println(x) };

BiConsumer<Integer, String> b = (Integer x, String y) -> System.out.println(x + " : " + y);

Predicate<String> p = (String s) -> { s == null };

@Functional interface is a newly added interface in Java 8, which is used to indicate that the interface type declaration is a functional interface defined according to the Java language specification. Java 8 also declares some functional interfaces that can be used by Lambda expressions. When the interface you annotate is not a valid functional interface, you can use @ functional interface to solve compilation level errors.

The following is a custom functional interface:

@FunctionalInterface 
public interface WorkerInterface {
   public void doSomeWork();
}

After the functional interface is defined, we can use it in the API and use Lambda expressions at the same time. For example:

//Define a functional interface
@FunctionalInterface
public interface WorkerInterface {

   public void doSomeWork();

}


public class WorkerInterfaceTest {

	public static void execute(WorkerInterface worker) {
   		worker.doSomeWork();
}

public static void main(String [] args) {

    //invoke doSomeWork using Annonymous class
    execute(new WorkerInterface() {
        @Override
        public void doSomeWork() {
            System.out.println("Worker invoked using Anonymous class");
        }
    });

    //invoke doSomeWork using Lambda expression 
    execute( () -> System.out.println("Worker invoked using Lambda expression") );
}

}

Output:

Worker invoked using Anonymous class 
Worker invoked using Lambda expression

Variable scope

Lambda expressions can only refer to external local variables marked final, which means that local variables defined outside the domain cannot be modified inside lambda, otherwise compilation errors will occur.

In java8 tester Java file, enter the following code:

public class Java8Tester {
 
   final static String salutation = "Hello! ";
   
   public static void main(String args[]){
      GreetingService greetService1 = message -> 
      System.out.println(salutation + message);
      greetService1.sayMessage("Runoob");
   }
    
   interface GreetingService {
      void sayMessage(String message);
   }
}

The output result is:

Hello! Runoob

We can also directly access the local variables of the outer layer in the lambda expression:

public class Java8Tester {
    public static void main(String args[]) {
        final int num = 1;
        Converter<Integer, String> s = (param) -> System.out.println(String.valueOf(param + num));
        s.convert(2);  // The output is 3
    }
 
    public interface Converter<T1, T2> {
        void convert(int i);
    }
}

The local variable of lambda expression can not be declared as final, but it must not be modified by the following code (i.e. implicit with final semantics)

int num = 1;  
Converter<Integer, String> s = (param) -> System.out.println(String.valueOf(param + num));
s.convert(2);
num = 5;  
//Error message: local variable num defined in an enclosing scope must be final or effective 
 final

It is not allowed to declare a parameter or local variable with the same name as a local variable in a Lambda expression.

String first = "";  
Comparator<String> comparator = (first, second) -> Integer.compare(first.length(), second.length());  //Compilation errors 

Java 8 method reference

Method references point to a method by its name.

Method reference can make the language structure more compact and concise and reduce redundant code.

Method references use a pair of colons::.

package com.runoob.main;
 
@FunctionalInterface
public interface Supplier<T> {
    T get();
}
 
class Car {
    //Supplier is jdk1 8 interface, which is used together with lamda
    public static Car create(final Supplier<Car> supplier) {
        return supplier.get();
    }
 
    public static void collide(final Car car) {
        System.out.println("Collided " + car.toString());
    }
 
    public void follow(final Car another) {
        System.out.println("Following the " + another.toString());
    }
 
    public void repair() {
        System.out.println("Repaired " + this.toString());
    }
}
  • Constructor reference: its syntax is Class::new, or more generally class < T >:: new. The examples are as follows:
final Car car = Car.create( Car::new );
final List< Car > cars = Arrays.asList( car );
  • Static method reference: its syntax is Class::static_method, an example is as follows:
cars.forEach( Car::collide );
  • Method reference of any object of a specific class: its syntax is Class::method. The example is as follows:
cars.forEach( Car::repair );
  • Method reference of a specific object: its syntax is instance::method. The examples are as follows:
final Car police = Car.create( Car::new );
cars.forEach( police::follow );

Method reference instance

import java.util.List;
import java.util.ArrayList;
 
public class Java8Tester {
   public static void main(String args[]){
      List<String> names = new ArrayList();
        
      names.add("Google");
      names.add("Runoob");
      names.add("Taobao");
      names.add("Baidu");
      names.add("Sina");
        
      names.forEach(System.out::println);
   }
}

In the example, we will use system The out:: println method is referenced as a static method.

Execute the above script and the output result is:

$ javac Java8Tester.java 
$ java Java8Tester
Google
Runoob
Taobao
Baidu
Sina

Keywords: Java

Added by suave4u on Thu, 17 Feb 2022 19:15:07 +0200