Take a look at the new feature of Java 8 -- lambda expression

What is a lambda expression?

Lambda expressions can be understood as a concise way to express transitive anonymous functions: it has no name, but it has a parameter list, function body, return type, and possibly a list of exceptions that can be thrown.

For example, the traditional way to write a new Thread is as follows

Thread t = new Thread(new Runnable() {
 public void run(){
 System.out.println("Hello world");
 }
}); 

So the way to write a lambda expression is

Thread t = new Thread(() -> System.out.println("Hello world")); 

->On the left is the parameter list, on the right is the function body

Functional interface

Why does @ FunctionalInterface annotation modify a class with only one abstract function

Looking at the source code of Java8, functions decorated by @ functional interface are called functional interfaces, such as Predicate. These classes often have only one abstract function. That's because "Lambda expressions are understood as concise expressions of transitive anonymous functions". When anonymous functions are used directly, no function name is specified. Therefore, if there are two or more abstract functions, The virtual machine doesn't know which method you want to execute. For example, the run() method of Runnable in the above example, we only use () in the parameter list part, and don't declare the function name of the call.

The functional interfaces of JDK are all in the path of java.util.function, and the common ones are

public interface Predicate<T>{
 boolean test (T t);
} 
public interface Consumer<T> {
 void accept(T t);
}
public interface Function<T, R> {
 R apply(T t);
}
...

Functional interface usage example

//Source code part
@FunctionalInterface
public interface Predicate<T>{
 boolean test(T t);
}
//Method construction
public static <T> List<T> filter(List<T> list, Predicate<T> p) {
 List<T> results = new ArrayList<>();
 for(T s: list){
 if(p.test(s)){
 results.add(s);
 }
 }
 return results;
}
//Using the example, filter out the data whose String is not empty through the filter method
Predicate<String> nonEmptyStringPredicate = (String s) -> !s.isEmpty();
List<String> nonEmpty = filter(listOfStrings, nonEmptyStringPredicate); 

Examples of other functional interfaces

Why do lambda expressions use local variables that must be final?

In addition to the data of parameter list, the body part of lambda expression can also use local variables outside the lambda expression, but these local variables can only be declared once, otherwise an error will be reported.

int portNumber = 1337;
//At this time, an error will be reported. The portNumber must be decorated with final
Runnable r = () -> System.out.println(portNumber);
portNumber = 31337; 

Because the body of A lambda expression can be regarded as an anonymous inner class, it needs to be final to access external local variables. From the perspective of thread, it means that the local variable is A thread (assumed to be called thread A), and the body of lambda expression is another thread (thread B). When thread A ends, thread B must access the data of thread A, which is definitely not possible. Therefore, the variables in thread B are not actually pointing to the variables in thread A, but copying one, so it must be ensured to copy out The data can't be changed.

Method reference

Another convenient place for lambda expression is method reference, which can be directly used in the form of class name:: method name.

for example

//Static method
Integer::parseInt
//Common methods for objects
String::length
//Construction method
Apple::new

The usage of compound lambda expression

lambda expressions can also be chained, with logical judgments of and or not (negate, and or)

//call chaining
inventory.sort(comparing(Apple::getWeight)
 .reversed()
 .thenComparing(Apple::getCountry));
 
//wrong
Predicate<Apple> notRedApple = redApple.negate();
//And
Predicate<Apple> redAndHeavyApple =
 redApple.and(a -> a.getWeight() > 150);
//or
Predicate<Apple> redAndHeavyAppleOrGreen =
 redApple.and(a -> a.getWeight() > 150)
 .or(a -> "green".equals(a.getColor())); 

Function composition

Function function interface provides two methods for continuous operation of data, and then and compose methods.

Function<Integer, Integer> f = x -> x + 1;
Function<Integer, Integer> g = x -> x * 2;
Function<Integer, Integer> h = f.andThen(g);
int result = h.apply(1);
//Output 3 = = >) (1 * 2) + 1

The andThen method is equivalent to executing the f function first and then the g function.

Function<Integer, Integer> f = x -> x + 1;
Function<Integer, Integer> g = x -> x * 2;
Function<Integer, Integer> h = f.compose(g);
int result = h.apply(1); 
//Output 4 = = > 1 + 1 * 2

The compose method is equivalent to executing the g function first and then the f function.

Next

Next, we will sort out the relevant knowledge points of the flow, and others (inject knowledge such as optional, new time tools, default methods, etc.).

Tags: Java Lambda JDK

Posted on Sun, 10 Nov 2019 07:36:10 -0500 by Carolyn