Java8 Lambda本質論

Lambda的本質

需求1. 按照產品的重量進行升序排序java

此處使用「匿名內部類」的設計,但摻雜了較多的語法噪聲,引入了沒必要要的複雜度。express

Collections.sort(repo, new Comparator<Product>() {
  @Override
  public int compare(Product p1, Product p2) {
    return p1.getWeight().compareTo(p2.getWeight());
  }
});

使用Lambda表達式,能夠進一步消除語法噪聲,簡化設計。app

Collections.sort(repo, (Product p1, Product p2) ->
  p1.getWeight().compareTo(p2.getWeight()));

也就是說,Lambda其本質是「匿名內部類」的一種「語法糖」表示,存在以下3個方面的特徵:ide

  • Anonymous Function:匿名的函數函數

  • Passed Around:可做爲參數或返回值進行傳遞,甚至能夠自由地存儲在變量中工具

  • Concise:相對於匿名內部類的樣板代碼(Boilerplate),Lambda更加簡潔漂亮this

類型推演

藉助編譯器「類型推演」的能力,能夠進一步簡化Lambda表達式。設計

Collections.sort(repo, (p1, p2) ->
  p1.getWeight().compareTo(p2.getWeight()));

Lambda的形式

  • 形式1:(parameters) -> expressioncode

Collections.sort(repo, (p1, p2) -> 
  p1.getWeight().compareTo(p2.getWeight()));
  • 形式2:(parameters) -> { statements; }排序

Collections.sort(repo, (p1, p2) -> {
  return p1.getWeight().compareTo(p2.getWeight());
});

默認方法

先看看java.util.Collections.sort的實現,其中java.util.Collections是一個典型的「工具類」。

public final class Collectins {
  private Collectins() {
  }
  
  public static <T> void sort(List<? extends T> l, Comparator<? super T> c) {
    l.sort(c);
  }
}

這樣的設計是反OO,爲此能夠將其sort搬遷至List接口中去。

public interface List<E> extends Collection<E> {
  default void sort(Comparator<? super E> c) {
    ...
  }
  ...
}

default方法相似於C++的虛函數。從某種意義上看,default的引入使得Java又從新回到了「多重繼承」的懷抱,爲設計帶來了更大的彈性。

爲此,設計可重構爲更加符合OO的風格。

repo.sort((p1, p2) -> p1.getWeight().compareTo(p2.getWeight()));

方法引用

藉助Comparator.comparing的工廠方法,結合「方法引用」可進一步提升代碼的可讀性。

import static java.util.Comparator.comparing;

repo.sort(comparing(Product::getWeight));

方法引用其本質是具備單一方法調用的lambda表達式的「語法糖」表示。

級聯方法

需求2. 按照產品的重量降序排序

repo.sort(comparing(Product::getWeight)
  .reversed());
  
  .thenComparing(Product::getCountry));

需求3. 若是重量相同,則按照出廠國的天然序排序

repo.sort(comparing(Product::getWeight)
  .reversed()
  .thenComparing(Product::getCountry));

深刻理解Comparator

有且僅有一個抽象方法的接口,稱爲「函數式接口」,使用@FunctionalInterface的註解標識。函數式接口中「抽象方法」描述了Lambda表達式的「原型」。

() -> {}也是一個合法的Lambda表達式,與Runnable接口相匹配。

也就是說,一個「函數式接口」可包含以下元素:

  • Abstract Method:有且僅有一個抽象方法

  • Default Methods0個或多個默認方法

  • Static Methods0個或多個靜態方法

對照前面的列子,可洞悉Comparator設計的巧妙。

repo.sort(comparing(Product::getWeight)
  .reversed());

其中,Comparator就是一個典型的函數式接口。經過「方法級聯」設計了一套簡單的ComparatorDSL,加強了用戶的表達力。

@FunctionalInterface
public interface Comparator<T> {
  int compare(T o1, T o2);
  
  default Comparator<T> reversed() {
    return Collections.reverseOrder(this);
  }
  
  static <T, U extends Comparable<? super U>> 
  Comparator<T> comparing(
    Function<? super T, ? extends U> extractor) {
    return (c1, c2) -> extractor.apply(c1)
      .compareTo(extractor.apply(c2));
  }
}

其中,Comprator.compring的實現稍微有點複雜。

  • comparing是一個靜態工廠方法,它生產一個Comparator<T>類型的實例;

  • comparing是一個高階函數;

    • 接受一個函數:Function<? super T, ? extends U> extractor

    • 返回一個函數:Comparator<T>

  • comparing是一個語法糖,結合「方法引用」的機制,極大地改善了用戶接口的表達力;

相關文章
相關標籤/搜索