Lambda expressions

1.1 the sample

Thread thread = new Thread(() -> {
    System.out.println("HelloWorld");
});
thread.start();
Copy the code

1.2 define

“Lambda expressions are understood as a succinct way of representing anonymous functions that are transitive, without a name, but with a list of arguments, a function body, a return type, and possibly a list of exceptions that can be thrown” (see Java8 in action)

A Lambda expression consists of three parts:

  • Parameter list: function parameter list
  • Arrow: Split argument list and body
  • Lambda body: Function body

1.2.1 Parameter List

Parameter lists use parentheses (x1,….) Where parameters may or may not be declared as parameter types (the compiler will infer from the context).

1.2.2 Function Body (Lambda body)

The function body of a Lambda expression can be either a code block or an expression. Code block: consistent with the body of a normal method; Expression: An expression is executed and returns a result, which is essentially a shorthand for a return statement (omitting return and {}).

1.2.3 Special Expressions

Lambda can not only omit parameter types and represent method bodies with expressions, but can also directly replace Lambda expressions with method references. For example, the following three ways are equivalent:

Consumer<String> consumer1 = (String str) -> {
    System.out.println(str);
};
Consumer<String> consumer2 = (str) -> {
    System.out.println(str);
};
Consumer<String> consumer3 = System.out::println;
Copy the code
  • I don’t quite understand the nature of the third one yet.

1.3 the characteristics of

Lambda expressions have the following characteristics:

  • Anonymous: Unlike normal methods, there is no explicit method name.
  • Functions: Lambda functions differ from methods in that they do not belong to a specific class.
  • Pass: Lambda expressions can be passed to methods as arguments or stored in variables.
  • Brevity: You don’t need to write as many complex functions as anonymous inner classes.

1.4 Lambda expressions and anonymous inner classes

Lambda expressions are used to provide an implementation for an abstract method that one can roughly understand as an anonymous inner class, but the two are not quite the same.

The same

They have the following similarities:

  • (Anonymous inner classes generate.class files, like Lambda expressions dynamically generate a class the first time it’s used?) ;
  • Have access to external variables and methods.
  • Only external final variables can be accessed, and external variables cannot be modified.
final String word = "Hello"; Runnable Runnable = () -> {// External variables cannot be modified in Lambda expressions, otherwise Variable Used will be promptedin lambda expression should be final or effectively final
    // word = "HelloWorld";
    function(1); System.out.println(word); }; runnable.run(); Runnable runnable1 = newRunnable() {
    @Override
    public void run() {
        function(1); Email exchange with respect to Variable is within inner class. Needs to be final or effectively final // word ="HelloWorld"; System.out.println(word); }}; runnable1.run();Copy the code

The difference between

  • Different implementation parent classes (or interfaces). Lambda expressions can only implement functional interfaces, while anonymous inner classes can implement interfaces with multiple abstract methods, abstract classes, and ordinary classes.
  • Anonymous inner classes can call other methods in the interface, but Lambda expressions cannot. (Essentially the second dot of the next difference)
  • bothDifferent scope. (1) In Lamda expressionthisRefers to the object of the outer class, while the inner classthisRefers to an inner class object. seeScope Example 1. (2) Methods called in Lamda expressions are also external, but methods called in inner classes take precedence over internal ones.
Scope Example 1
public class HelloLambda {

    Runnable r1 = () -> {
        System.out.println("Lambda, 1: " + this);
        System.out.println("Lambda, 2: " + toString());
    };

    Runnable r2 = new Runnable() {
        @Override
        public void run() {
            System.out.println("Inner class, 1: " + this);
            System.out.println("Inner class, 2: "+ toString()); }}; @Override public StringtoString() {
        return "Hello, lambda!"; } public static void main(String[] args) { new HelloLambda().r1.run(); new HelloLambda().r2.run(); }}Copy the code

Output:

Scope Example 2

Lambda expression R1 is faulty because there is no run() method in the outer class; R2, however, has a run() method in its inner class, so it can be compiled and essentially called in a loop.

2. functional interfaces

A functional interface is one that has one and only one abstract method. Lambda expressions allow implementations of abstract methods of functional interfaces to be provided directly inline, with the entire expression as an instance of the functional interface. Lambda expressions can be thought of as anonymous inner classes that implement functional interfaces.

Special note:

  • Functional interfaces can have non-abstract methods in addition to specific abstract methods.
  • If there is an abstract method in the interface that overrides Obejct’s public method, that method also does not affect the count of the functional interface abstract methods.

Next, several common functional interfaces are introduced.

2.1 Runnable and Callable interfaces

Runnable

Runnable interface source code

@FunctionalInterface
public interface Runnable {
    public abstract void run();
}
Copy the code

Looking at the Runnable source code, you can see that the interface has only one abstract method run(), so you can use a Lambda expression as an instance of the functional interface and provide an implementation of the abstract method.

Runnable runnable = () -> {
    System.out.println("Hello world!");
};
Copy the code

Callable

Callable interface source code:

@FunctionalInterface
public interface Callable<V> {
    V call() throws Exception;
}
Copy the code

Examples of Lambda expressions for the Callable interface:

Callable<String> callable = () -> {
    return "HelloWorld";
};
Copy the code

The Callable interface has a return value, and the Lambda body can directly use an expression instead of a return statement, as follows:

Callable<String> callable = () -> "HelloWorld";
Copy the code

2.2 the Comparator interface

Source code (part)

@functionalInterface public interface Comparator<T> {// compare(T o1, T o2); Bool equals(Object obj); // Override the abstract method of the Obejct class public method without counting. // Non-abstract method default Comparator<T>reversed() {
        returnCollections.reverseOrder(this); }}Copy the code

If you look at the source code, you can see that the Comparator implementation class (1) needs to declare generics; (2) The compare method needs to be implemented, and there are two parameter lists. The parameter types are the same as declared in the generic class.

Example Lambda expressions

A simple example

Comparator<Integer> comparator = (o1, o2) -> o1.compareTo(o2);
Copy the code

Example of sorting a list in reverse order:

List<Integer> numbers = Lists.newArrayList(1, 2, 3);
numbers.sort((x1, x2) ->  x2.compareTo(x1));
numbers.forEach((x) -> {
    System.out.println(x);
});
Copy the code

Output:

Sort by object properties

Sort by user in reverse order of age:

List<User> users = Lists.newArrayList(new User(18), new User(22), new User(24));
users.sort((u1, u2) -> u2.age.compareTo(u1.age));
users.forEach((u) -> {
    System.out.println(u.age);
});
Copy the code

Output:

2.3 Consumer

The Consumer interface is a Consumer interface that takes parameters and returns no values.

The source code

@FunctionalInterface
public interface Consumer<T> {
    void accept(T t);
    
    default Consumer<T> andThen(Consumer<? super T> after) {
        Objects.requireNonNull(after);
        return(T t) -> { accept(t); after.accept(t); }; }}Copy the code

The Consume interface can be understood as a general-purpose consumer interface (it can accept arguments, but not return them) that defines actions to be performed through lambda expressions and calls the Accept method.

A simple example

Consumer<String> consumer = s -> System.out.println(s);
consumer.accept("Hello world1!");
Copy the code

This is better compared to the Runnable interface, which can also execute the run method directly (without the threading feature), but cannot accept the parameters passed in. To compare

Consumer<String> consumer = s -> System.out.println(s);
consumer.accept("Hello world1!");

Runnable runnable = () -> {
    System.out.println("Hello world2!");
};
runnable.run();
Copy the code

The callback example

Personally, the most common use of the Consumer interface is as a callback. As shown below, function1 accepts the argument list and the callback consumer. Function1 itself does not care about processing, but simply executes consumer.accept(), which is determined by the consumer variable passed in (lambda expression, callback function). You can modify the implementation of the function by modifying the consumer variable passed in.

public static void function1(List<Integer> list, Consumer<List<Integer>> consumer) { consumer.accept(list); } public static void main(String[] args) {Consumer<List<Integer>> Consumer1 = List -> {list.sort((x1, x1)) x2) -> x2.compareTo(x1)); }; Consumer<List<Integer>> consumer2 = list -> { list.sort((x1, x2) -> x1.compareTo(x2)); }; List<Integer> numbers = Lists.newArrayList(1, 2, 3); // Pass lambda expressionsfunction1(numbers, consumer1);
    numbers.forEach((x) -> {
        System.out.println(x);
    });
    
    function1(numbers, consumer2);
    numbers.forEach((x) -> {
        System.out.println(x);
    });
}
Copy the code

BiConsumer

The BiConsumer interface is an updated version of the Consumer interface that accepts two parameters:

BiConsumer<String, Integer> biConsumer = (str, num) -> {
    System.out.println(str + num);
};
biConsumer.accept("Hello, ", 123);
Copy the code

There are other consumer interfaces, such as IntConsumer.

2.4: Supplier interface

The Supplier interface is a supply interface with no parameters and a return value. Can be understood as a container that generates and stores objects for other methods to call the get() method to get objects.

The source code

@FunctionalInterface
public interface Supplier<T> {
    T get();
}
Copy the code

A simple example

Supplier<User> supplier = () -> {
    return new User(123);
};
User user = supplier.get();
System.out.println(user.age);
Copy the code

The callback example

As shown below, the getId method is used to return an int, but different places may use different generation policies, so call back the ID generation policy using the Supplier interface as an argument.

public static Integer getId(Supplier<Integer> supplier) {
    return supplier.get();
}

public static void main(String[] args) {
    Supplier<Integer> supplier1 = () -> (new Random()).nextInt(10);
    Supplier<Integer> supplier2 = () -> (new Random()).nextInt(100);
    System.out.println(getId(supplier1));
    System.out.println(getId(supplier2));
}
Copy the code

Personally, however, the Supplier interface has very little meaning. It cannot accept parameters and return directly. Even as a callback, it is difficult to have many actual use scenarios.

2.5 Predicate interface

Predicate interface A Predicate interface that has parameters that return values of type Boolean.

The source code

@FunctionalInterface
public interface Predicate<T> {

    boolean test(T t);

    default Predicate<T> and(Predicate<? super T> other) {
        Objects.requireNonNull(other);
        return (t) -> test(t) && other.test(t);
    }

    default Predicate<T> negate() {
        return (t) -> !test(t);
    }

    default Predicate<T> or(Predicate<? super T> other) {
        Objects.requireNonNull(other);
        return (t) -> test(t) || other.test(t);
    }

    static <T> Predicate<T> isEqual(Object targetRef) {
        return(null == targetRef) ? Objects::isNull : object -> targetRef.equals(object); }}Copy the code

A look at the source code shows that you need to implement the test() method and also support and/or joint judgment conditions.

Use the sample

Predicate<Integer> predicate = (x) -> x < 100;
Predicate<Integer> predicate1 = (x) -> x > 0;
System.out.println(predicate.test(1));
System.out.println(predicate.and(predicate1).test(-1));
Copy the code

Output result:

2.6 the Function interface

The source code

@FunctionalInterface
public interface Function<T, R> {
    R apply(T t);

    default <V> Function<V, R> compose(Function<? super V, ? extends T> before) {
        Objects.requireNonNull(before);
        return (V v) -> apply(before.apply(v));
    }

    default <V> Function<T, V> andThen(Function<? super R, ? extends V> after) {
        Objects.requireNonNull(after);
        return (T t) -> after.apply(apply(t));
    }

    static <T> Function<T, T> identity() {
        returnt -> t; }}Copy the code

A look at the source code shows that the apply() method needs to be implemented, and the lambda expression argument

, where T represents the parameter type of the function and R represents the return type (the first is the parameter, the second is the return value). Function interface using the scene more Consumer/Predicate / : Supplier interface can realize the Function of the use Function interface can be achieved.
,>

A simple example

Function<Integer, String> function = (x) -> {
    if (x < 0) {
        return "Less than zero";
    } else {
        return "Greater than or equal to zero"; }}; System.out.println(function.apply(3));Copy the code

BiFunction

The Function interface also has a BiFunction interface for receiving two parameters.

BiFunction<Integer, Integer, String> function = (x1, x2) -> {
    return new StringBuilder("Receive x1=").append(x1).append(", x2=").append(x2).toString();
};
Copy the code

3. The flow

Java8 adds a new interface java.util.Stream, which can process Collection, List, Set, Map, etc., using streams, making coding easier and faster. The Stream interface relies on Lambda expressions. See:……