Java官方筆記9Lambda表達式

Lambda Expression

有了Lambda Expression,就不用再寫anonymous classes。

寫Lambda,首先要找到它的類型。

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

函數接口,只有1個抽象方法的接口:

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

默認方法do not count,所以下面這個也是函數接口:

@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
    }
}

靜態方法也do not count,所以下面這個還是函數接口:

@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
    }
}

接着,實現函數接口的抽象方法,比如Predicate的抽象方法:

boolean test(T t);

實現Predicate<String>

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

省略了返回類型和方法名,只保留參數列表和方法體,用->連接。

再簡化:

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

這就是最常見的Lambda,因爲前面有String,編譯器能夠知道,所以這裏把(String s)簡寫爲s,然後只有一行,再把大括號和return省略掉。

無return,跟省略掉return:

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

無參數,用()代替:

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

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;
}

因爲Lambda實現了接口的抽象方法,所以predicate.test(s)調用的實際上是Lambda。

Lambda不能修改變量值:

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/

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章