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.).