Java Official Notes 9 Lambda Expressions

Lambda Expression

With Lambda Expression, there is no need to write anonymous classes.

To write Lambda, you must first find its type.

There is a restriction on the type of a lambda expression: it has to be a functional interface.

Functional interface, an interface with only 1 abstract method:

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

The default method do not count, so the following is also a functional interface:

@FunctionalInterface
public interface Consumer<T> {

    void accept(T t);

    default Consumer<T> andThen(Consumer<? super T> after) {
        // the body of this method has been removed
    }
}

Static methods also do not count, so the following is still a functional interface:

@FunctionalInterface
public interface Predicate<T> {

    boolean test(T t);

    default Predicate<T> and(Predicate<? super T> other) {
        // the body of this method has been removed
    }

    default Predicate<T> negate() {
        // the body of this method has been removed
    }

    default Predicate<T> or(Predicate<? super T> other) {
        // the body of this method has been removed
    }

    static <T> Predicate<T> isEqual(Object targetRef) {
        // the body of this method has been removed
    }

    static <T> Predicate<T> not(Predicate<? super T> target) {
        // the body of this method has been removed
    }
}

Next, implement the abstract method of the functional interface, such as the abstract method of Predicate:

boolean test(T t);

Achieve Predicate<String>:

Predicate<String> predicate =
    (String s) -> {
        return s.length() == 3;
    };

The return type and method name are omitted, leaving only the parameter list and method body, with ->concatenation.

Simplify again:

Predicate<String> predicate = s -> s.length() == 3;

This is the most common Lambda. Because there is a String in front, the compiler can know it, so here it is (String s)abbreviated as s, and then there is only one line, and the curly braces and return are omitted.

No return, and return omitted:

Consumer<String> print = s -> System.out.println(s);

No arguments, ()replace with:

Runnable runnable = () -> System.out.println("I am running");

Use of Lambda:

List<String> retainStringsOfLength3(List<String> strings) {

    Predicate<String> predicate = s -> s.length() == 3;
    List<String> stringsOfLength3 = new ArrayList<>();
    for (String s: strings) {
        if (predicate.test(s)) {
            stringsOfLength3.add(s);
        }
    }
    return stringsOfLength3;
}

Because Lambda implements the abstract method of the interface, it predicate.test(s)is actually Lambda that is invoked.

Lambda cannot modify variable values:

int calculateTotalPrice(List<Product> products) {

    int totalPrice = 0;
    Consumer<Product> consumer =
        product -> totalPrice += product.getPrice();  // 编译错误
    for (Product product: products) {
        consumer.accept(product);
    }
}

编译错误:Variable used in lambda expression should be final or effectively final

This process of accessing variable is called capturing: lambdas cannot capture variables, they can only capture values. A final variable is in fact a value.

也就是Lambda只能读,不能写。

使用Lambda

Lambda是JDK8跨时代的语法技术,它引领了大量的JDK API重写。

java.util.function中包含了很多可供实现的函数接口,functional interfaces:

https://docs.oracle.com/en/java/javase/20/docs/api/java.base/java/util/function/package-summary.html

比如:

@FunctionalInterface
public interface Supplier<T> {

    T get();
}

Lambda实现:

Supplier<String> supplier = () -> "Hello Duke!";`

使用:

Random random = new Random(314L);
Supplier<Integer> newRandom = () -> random.nextInt(10);

for (int index = 0; index < 5; index++) {
    System.out.println(newRandom.get() + " ");
}

比如:

List<String> strings = ...; // really any list of any kind of objects
Consumer<String> printer = s -> System.out.println(s);
strings.forEach(printer);

比如:

List<String> immutableStrings =
        List.of("one", "two", "three", "four", "five");
List<String> strings = new ArrayList<>(immutableStrings);
Predicate<String> isOddLength = s -> s.length() % 2 == 0;
strings.removeIf(isOddLength);
System.out.println("strings = " + strings);

比如:

@FunctionalInterface
public interface Function<T, R> {

    R apply(U u);

    // default and static methods removed
}
Function<String, Integer> toLength = s -> s.length();
String word = ...; // any kind of word will do
int length = toLength.apply(word);

以上是JDK中最经典的4种function interface

  • Supplier<T>
  • Consumer<T>
  • Predicate<T>
  • Function<T, R>

总结一下,Lambda的使用就是先实现function interface的abstract method,然后①接口实例调用abstract method,或者②接口实例被forEach等调用。

Lambda使得function interface能够实例化:Predicate<String> isOddLength = s -> s.length() % 2 == 0;

The java.util.function package is now central in Java, because all the lambda expressions you are going to use in the Collections Framework or the Stream API implement one of the interfaces from that package.

发现没有,把:

List<String> strings = ...; // really any list of any kind of objects
Consumer<String> printer = s -> System.out.println(s);
strings.forEach(printer);

简写一下,去掉中间的接口实例:

List<String> strings = ...; // really any list of any kind of objects
strings.forEach(s -> System.out.println(s));

这不就是常见到的Lambda的形态嘛。换句话说,Lambda的结果就是函数接口实例(Function interface instance),使用Lambda本质上就是调用函数,Java中没有函数的概念,通过function interface的abstract method,引入了函数,从而造就了Lambda。

Method References

Sometimes people call these lambda expressions "anonymous methods", since it is just that: a method that has no name, and that you can move around your application, store in a field or a variable, pass as an argument to a method or a constructor and return from a method.

Consumer<String> printer = s -> System.out.println(s);

这里的Lambda没有什么逻辑,其实就是System.out.println()方法的引用,所以可以简写为Bound(System.out优化前后一致):

Consumer<String> printer = System.out::println;

Static:

IntBinaryOperator max = (a, b) -> Integer.max(a, b);
IntBinaryOperator max = Integer::max;

Unbound(优化前s,优化后String;优化前user,优化后User):

Function<String, Integer> toLength = s -> s.length();
Function<String, Integer> toLength = String::length;
Function<User, String> getName = user -> user.getName();
Function<String, Integer> toLength = User::getName;
BiFunction<String, String, Integer> indexOf = (sentence, word) -> sentence.indexOf(word);
BiFunction<String, String, Integer> indexOf = String::indexOf;

Constructor:

Supplier<List<String>> newListOfStrings = () -> new ArrayList<>();
Supplier<List<String>> newListOfStrings = ArrayList::new;

总结:

Combining Lambda Expressions

Function interface中的defualt method该登场了。

Predicate<String> nonNull = s -> s != null;
Predicate<String> nonEmpty = s -> s.isEmpty();
Predicate<String> shorterThan5 = s -> s.length() < 5;

Predicate<String> p = nonNull.and(nonEmpty).and(shorterThan5);

这里的and()就是default method,它实现了Lambda的组合,也就是链式调用的既视感。

稍复杂点的:

Predicate<String> isNull = Objects::isNull;
Predicate<String> isEmpty = String::isEmpty;
Predicate<String> isNullOrEmpty = isNull.or(isEmpty);
Predicate<String> isNotNullNorEmpty = isNullOrEmpty.negate();
Predicate<String> shorterThan5 = s -> s.length() < 5;

Predicate<String> p = isNotNullNorEmpty.and(shorterThan5);

Comparators

@FunctionalInterface
public interface Comparator<T> {

    int compare(T o1, T o2);
}

Lambda实现:

Comparator<Integer> comparator = (i1, i2) -> Integer.compare(i1, i2);
Comparator<Integer> comparator = Integer::compare;

Comparator的静态方法:

Comparator<String> comparator = Comparator.comparing(String::length);

This comparing() method is a static method of the Comparator interface. It takes a Function as an argument, that should return a type that is an extension of Comparable.

使用:

List<User> users = ...; // this is your list
Comparator<User> byName = Comparator.comparing(User::getName);
users.sort(byName);

还有默认方法thenComparing:

Comparator<User> byFirstNameThenLastName =
        Comparator.comparing(User::getFirstName)
                  .thenComparing(User::getLastName);

参考资料:

Lambda Expressions https://dev.java/learn/lambdas/

Guess you like

Origin blog.csdn.net/weixin_45741835/article/details/131267965