java8-01-lambda

[TOC]java

0 lambda的傳說

其實我也無法說清楚他究竟是什麼鬼……程序員

就比如你沒見過某種顏色,我再怎麼描述都無法描述清楚,仍是親自看看他長啥樣吧app


還記得中學的時候,數學裏常常出現的那個符號嗎?ide

先放一個從百度圖庫偷來的圖片(CSDN打上的水印,不關我事哈……)函數

lambda

就是這貨,此處的lambda 就是它…… 測試

傳說中這玩意兒是比程序員還瘋狂的數學家的口頭禪……spa

上面的廢話都看完了????線程

雖然見到了他,可是仍是不清楚他究竟是什麼……scala

好了,不扯皮了,開講啦code


  • 百科裏是這麼描述的:

Lambda 表達式」是一個匿名函數,能夠包含表達式和語句,而且可用於建立委託或表達式目錄樹類型。

看百科的描述好像有那麼點感受了。但是幹嗎非要搞個這麼高冷的解釋呢?

  • 我的是這麼理解的:

lambda 相似於一個可調用的代碼塊或者遊離函數。固然也能夠有入參。

1 瞄一眼他長啥樣?

  • 示例

好比下面這樣:

Comparator<Integer> c = (i, j) -> Integer.compare(i, j);

等價的原始Java代碼長這樣:

Comparator<Integer> c = new Comparator<Integer>() {
  @Override
  public int compare(Integer o1, Integer o2) {
    return Integer.compare(o1, o2);
  }
};

用上lambda是什麼體驗?

  • 代碼量好像少了

  • 逼格好像提升了

  • 代碼好像更優雅了

2 lambda各類外觀

至少在Java裏lambda的爸爸媽媽姐姐弟弟爺爺奶奶七大姑八大姨……可能都是長這個樣子的。

同時,據老夫多年的斷案經驗來推斷,lambda的本尊應該也是這個樣子的:

(Type1 param1,Type2 param2, ...) -> {
  // 一些亂七八糟、烏漆嘛黑的處理操做();
  return ret;
}
  • 外觀一:沒有入參的時候

()->{return ret;}
  • 外觀二:有參數的時候

(Type p)->{return ret;}

// 參數類型能夠省略(隱式推掉)
(p)->{return ret;}

// 一個參數的時候,參數列表外的括號也能夠沒有
p->{return ret;}
  • 外觀三:函數體只有一行的時候

// 方法體只有一行的時候,或括號連同結尾的分號均可以省略
e->ret
  • 外觀四:沒有返回值的時候

e->{}
  • 方法引用和構造器引用

    • instance::instanceMethod

    • className::staticMethod

    • className::instanceMethod

Arrays.asList(null, "", "   ", "HelloWorld", "ByeBug")//
  .stream().filter(StringUtils::isNotBlank)//
  .forEach(System.out::println);
  • ……

3 lambda使用場景

再看一個例子:

// 原始方法啓動一個線程
new Thread(new Runnable() {
  @Override
  public void run() {
  }
}).start();

// lambda版
new Thread(()->{}).start();

不難看出,整個匿名內部類中最關鍵的代碼其實就是:

public void run() {
}

因此,lambda中關鍵部分也就是這部分代碼了。

其實,用註解java.lang.FunctionalInterface修飾的接口均可以用於lambda表達式中。

這種接口,都是隻有一個方法的接口。

另外,只要你的接口只有一個方法,即便是沒有@FunctionalInterface註解修飾,也是能夠用lambda的(不少時候編譯器仍是很聰明的,他會自動推斷的)。

總之,lambda只鍾愛函數式接口(@FunctionalInterface)。


4 再來個示例

public class HouseInfo {

    private Integer houseId; // 小區ID
    private String houseName;// 小區名
    private Integer browseCount; // 瀏覽數
    private Integer distance;// 距離 KM
    // constructor
    // getters
    // setters
}
  • 有以下數據

List<HouseInfo> houseInfos = Lists.newArrayList(//
    new HouseInfo(1, "恆大星級公寓", 100, 1), //
    new HouseInfo(2, "匯智湖畔", 999, 2), //
    new HouseInfo(3, "張江湯臣豪園", 100, 1), //
    new HouseInfo(4, "保利星苑", 23, 10), //
    new HouseInfo(5, "北顧小區", 66, 23), //
    new HouseInfo(6, "北傑公寓", null, 55), //
    new HouseInfo(7, "保利星苑", 77, 66), //
    new HouseInfo(8, "保利星苑", 111, 12)//
);
  • 按距離排序

Collections.sort(houseInfos, (h1, h2) -> {
    if (h1 == null || h2 == null)
      return 0;
    if (h1.getDistance() == null || h2.getDistance() == null)
      return 0;
    return h1.getDistance().compareTo(h2.getDistance());
});
  • 輸出小區名

houseInfos.stream().map(h -> h.getHouseName()).forEach(System.out::println);
  • 動態條件過濾

// 該函數式接口用來當作測試條件
public static interface Condition<T> {
  boolean conditionTest(T e);
}

// 定義一個方法來輸出知足condition測試結果的小區
public void printWithFilter(List<HouseInfo> houseList, Condition<HouseInfo> condition) {
  houseList.stream().filter(e -> condition.conditionTest(e)).forEach(System.out::println);
}

@Test
public void test4() {
  List<HouseInfo> houseInfos = Lists.newArrayList(//
    new HouseInfo(1, "恆大星級公寓", 100, 1), //
    new HouseInfo(2, "匯智湖畔", 999, 2), //
    new HouseInfo(3, "張江湯臣豪園", 100, 1), //
    new HouseInfo(4, "保利星苑", 23, 10), //
    new HouseInfo(5, "北顧小區", 66, 23), //
    new HouseInfo(6, "北傑公寓", null, 55), //
    new HouseInfo(7, "保利星苑", 77, 66), //
    new HouseInfo(8, "保利星苑", 111, 12)//
  );
  // 打印小區名包含北字的小區
  printWithFilter(houseInfos, e -> e != null && e.getHouseName().contains("北"));
  // 打印距離大於10KM的小區
  printWithFilter(houseInfos, e -> e != null && e.getDistance() != null && e.getDistance() > 10);
}
  • 字符串轉大寫

Arrays.asList("HelloWord", "ByeBug")
  .stream().map(String::toUpperCase).forEach(System.out::println);
  • 其實在Scala裏lambda更加直觀

List("HelloWord", "ByeBug").map(_.toUpperCase()).foreach(println _);

5 lambda的好基友

經過上面的示例,再結合實際。不難發現最經常使用的lambda的形式以下:

  • 單個輸入,無輸出

  • 單個輸入,單個輸出

  • 無輸入,輸出單個類型

  • 兩個不一樣類型的輸入,第三種類型的輸出

  • 兩個不一樣類型的輸入,其中一種類型的輸出

  • ……

其實在每次使用的時候,不必本身去新建這些函數式接口以支持lambda,JDK就已經給lambda內置了不少好基友:

  • Consumer: 單個輸入,無輸出

public interface Consumer<T> {
  void accept(T t);
}
  • BiConsumer: 兩個不一樣類型的輸入,無輸出

public interface BiConsumer<T, U> {
  void accept(T t, U u);
}
  • Supplier: 無輸入,輸出單個類型

public interface Supplier<T> {
  T get();
}
  • Function: 兩個不一樣類型的輸入

public interface Function<T, R> {
  R apply(T t); // 輸入T類型,轉換成R類型
}
  • ToIntFunction / ToLongFunction / ToDoubleFunction

public interface ToIntFunction<T> {
    // T類型的輸入,輸出int 
    // 相似於Stream的mapToInt()
    int applyAsInt(T value); 
}
  • IntFunction / LongFunction / DoubleFunction

public interface IntFunction<R> {
    R apply(int value);
}
  • BiFunction: 兩個不一樣類型的輸入,第三種類型的輸出

public interface BiFunction<T, U, R> {
  R apply(T t, U u);
}
  • 條件測試

public interface Predicate<T> {
  boolean test(T t);
}

能夠不用自定義函數式接口來支持lambda,上面的例子能夠改爲:

public void printWithFilter(List<HouseInfo> houseList, Predicate<HouseInfo> condition) {
  houseList.stream().filter(e -> condition.test(e)).forEach(System.out::println);
}

關於lambda的傳說和他究竟是什麼鬼,看到這裏應該夠了。

畢竟咱們的主要目的是使用它,知道他怎麼用纔是重點。

不必糾結他嚴格的學術定義(這種事不該該是那種只會紙上談兵的老不死乾的嗎?)。

在Java8裏和lambda相關的主要API就是Stream了。在瞭解Stream的時候來順便熟悉lambda吧。

相關文章
相關標籤/搜索
本站公眾號
   歡迎關注本站公眾號,獲取更多信息