New Java8 feature: Lambda expression

Keywords: Java Lambda

Lambda expressions can be understood as anonymous functions: they have no name, but have parameter lists, function bodies, and return types.It is an implementation of behavior parameterization, which refers to passing different behaviors as parameters to a method whose capabilities depend on the behavior parameters it receives.Using Lambda expressions allows us to cope with changing needs without having to write a fixed set of implementation classes for these behaviors. Until 1.8, anonymous inner classes were used to achieve the same effect, but the way anonymous inner classes behaved was tedious relative to Lambda expressions.

Functional Interface

The use of Lambda expressions depends on functional interfaces, and Lambda expressions can only be used where functional interfaces are accepted.Functional interfaces are interfaces that declare only one abstract method and can have multiple static and default methods, as follows:

@FunctionalInterface
public interface Calculation {
    int calculate(int a, int b);
}

The @FunctionalInterface annotation indicates that the labeled interface will be designed as a functional interface, which is not required. It is mainly due to compilation errors when the interface violates functional interface principles.For example, modifying the Calculation interface and adding an abstract method will result in a Multiple non-overriding abstract methods found in interface com.cf.demo.lambda.Calculation compilation error:

//Compilation error: Multiple non-overriding abstract methods found in interface com.cf.demo.lambda.Calculation
@FunctionalInterface
public interface Calculation {
    int calculate(int a, int b);

    int calculate2(int a, int b);
}

Note: Object class methods are exceptions, and even if an interface declares methods of multiple Object classes, they will not be counted as declaring only one abstract method.The following Calculation interface is the correct functional interface:

@FunctionalInterface
public interface Calculation {
    int calculate(int a, int b);

    boolean equals(Object obj);
}

Java8 provides some commonly used functional interfaces under the java.util.function package, and to avoid boxing operations, provides interfaces corresponding to basic types, which are built-in functional interfaces that we can use first in practice.Of course, there are some cases where we need to use a custom functional interface, such as when we need to throw exceptions in Lambda expressions, where we need to customize a functional interface and declare exceptions.

Lambda expression syntax

Lambda expression consists of a list of parameters, an arrow (Lambda operator), and a body of Lambda.The parameter list of the Lambda expression corresponds to the parameter list of the functional interface, and the return value of the Lambda body corresponds to the return type of the functional interface.There are now the following doArithmetic methods, which receive two integer parameters and a Calculation. The behavior of the doArithmetic method is determined by the passed Calculations, which we can call to pass different Calculations to complete different calculations:

    public static int doArithmetic(int a, int b, Calculation calculation){
        return calculation.calculate(a, b);
    }

Now, to calculate the product of two numbers, use the internal class method:

	int result = doArithmetic(3, 2, new Calculation() {
            @Override
            public int calculate(int a, int b) {
                return a * b;
            }
	});
	System.out.println(result);//6

Use Lambda expressions in a more concise way:

	int result = doArithmetic(3, 2, (int a, int b) -> a * b);
	System.out.println(result);//6

(int a, int b) is the parameter list part of the Lambda expression. You can omit parentheses when there is only one parameter. There are many parameters, so keep parentheses.Parameter types can be omitted because the Java compiler can infer the data type from the context without displaying the declaration:

	int result = doArithmetic(3, 2, (a, b) -> a * b);
	System.out.println(result);//6

When the Lambda body has only one statement, it is possible to omit {} and return, (int a, int b) -> a * b) which are subsequent to omitting. We can also use the complete writing:

	int result = doArithmetic(3, 2, (a, b) -> {
            return a * b;
	});
	System.out.println(result);//6

When you need to use an'external local variable'in a Lambda expression, the'external local variable' defaults to final, and the'external local variable'here refers to a local variable that is not defined internally in the Lambda expression.Modify the doArithmetic method, add an'external local variable'and assign an initial value to the product. The following code does not compile:

	int initialValue = 1;
	int result = doArithmetic(3, 2, (a, b) -> a * b + initialValue);
	initialValue = 2;//Variable used in lambda expression should be final or effectively final
	System.out.println(result);

Method Reference

Method references can simplify Lambda expressions under'a special case','a special case'means that something else Lambda expression does is implemented, so we can use this method directly and pass it like a Lambda expression.The syntax of a method reference is to place the target reference in the delimiter:: Before, the name of the method is to be followed, and the target reference can be either a class name or an object name.Three examples are provided to illustrate the use of method references, with the addition of an Arithmetic class, which contains a static method and an instance method:

	public class Arithmetic {
		public static int multiply(int a, int b){
			return a * b;
		}

		public int add(int a, int b){
			return a + b;
		}
	}

1. Method references to static methods

	int multiplyResult = doArithmetic(3, 2, Arithmetic::multiply);
	System.out.println(multiplyResult);//6

2. Method references to instance methods pointing to existing objects

	Arithmetic arithmetic = new Arithmetic();
	int addResult = doArithmetic(3, 2, arithmetic::add);
	System.out.println(addResult);//5

3. Method references to any type of instance method, which have the characteristic of referencing a method of an object that is itself a parameter of Lambda.For example, now you need to compare the sizes of two numbers by first modifying the calculate method parameter type to wrapper type Integer:

	@FunctionalInterface
	public interface Calculation {
		int calculate(Integer a, Integer b);
	}

Comparing the sizes of a and b can be written as follows:

	int result = doArithmetic(3, 2, Integer::compareTo);//Integer::compareTo equals a.compareTo(b) 
	System.out.println(result);//1

Constructor Reference

For an existing constructor, you can use its name and new to create a reference to it: ClassName::new.When using constructor references again, the list of constructor parameters that need to be invoked should be consistent with the parameters of the abstract method of the functional interface.For example, two methods have been added to generate String objects:

    public static String generateString(Supplier<String> supplier) {
        return supplier.get();
    }

    public static String generateString(String value, Function<String, String> function) {
        return function.apply(value);
    }

Use constructor references:

	String result = generateString(String::new);//Call String() construction method
	System.out.println(result);

	result = generateString("hello Lambda", String::new);//Call String(String original) construction method
	System.out.println(result);

Posted by LDM2009 on Thu, 23 Apr 2020 18:05:13 -0700