1、介紹
本片文章會從一下幾個知識點進行介紹:
- 函數式接口 @FunctionalInterface
- Lambda 表達式
- 函數引用 Function Reference
- Stream
看了幾篇關于 java8 入門的例子,其中引入了許多令人期待已久的特性(雖然我沒有過這樣的體會),但不管你的代碼是普通青年款還是文藝青年款,你都可以從被 java8 的重新組織的代碼看到她的簡潔之處,不得不讓我對這些新入貴圈(java圈)的小鮮肉做一些記錄,以便我能很好的利用他們。
最大的一個特點莫非就是引入了“函數式”編程這一個概念(都是剽竊的別的語言來的),還有那么多新奇的詞語,比如“語法糖”“閉包”“顯式|隱式函數”等這里我就不再一個一個去體會了,這里屬于初次接觸,先理解會用為主,細節的深入待后期慢慢琢磨。
?
2、函數式接口 -- @Functionnalinterface
在翠花正式上代碼示例之前,我們現來對這個函數式接口有個認識:
- 接口必須只有一個抽象方法;
- 接口可以有其他的默認(default)或者靜態(static)方法;
- 接口默認繼承了 Object 類,所以接口中不能覆蓋了 Object 中的方法;
說明:該注解不是必須的,如果一個接口滿足“函數式接口”特性,那么不加這個注解也沒有影響,加上該注解能夠更好的讓編譯器檢查,如果不符合規范將會報編譯錯誤。
比如在 jdk8 中就對Comparator、Callable、Runnable等接口加上了該注解,下面兩段代碼的效果是一樣的:
1 public static void runThreadByLambda() { 2 Runnable runnable = () -> System.out.println("這個是用拉姆達實現的線程"); 3 new Thread(runnable).start(); 4 } 5 6 //--------------------------------------------------------------------------------- 7 public static void runThreadByInnerClass() { 8 Runnable runnable = new Runnable() { 9 @Override 10 public void run() { 11 System.out.println("這個是用內部類實現的線程"); 12 } 13 }; 14 new Thread(runnable).start(); 15 }
?3、Lambda 表達式
先用自己的語言來個 Lambda 下個定義:"一段帶有輸入參數的可執行語句塊”。精確與否那是別人的事情,反正我是這么理解的(目前階段是這樣的)。
1 (Type1 param1, Type2 param2, ..., TypeN paramN) -> { 2 statment1; 3 statment2; 4 //............. 5 return statmentM; 6 }
對應定義我們自然要給個例子
1 List<String> names = new ArrayList<>(); 2 names.add("TaoBao"); 3 names.add("ZhiFuBao"); 4 List<String> lowercaseNames = names.stream().map((String name) -> {return name.toLowerCase();}).collect(Collectors.toList());
這不就是將一個集合全部元素變為小寫嗎?和我以前用的循環代碼繁簡度不想上下,有什么好奇怪的!這位看管不要著急嘛,下面我來個他搓個背您再看看如何。
1 List<String> lowercaseNames = names.stream().map(name -> name.toLowerCase()).collect(Collectors.toList()); 2 3 //如果加入方法應用就是這樣 4 List<String> lowercaseNames = names.stream().map(String::toLowerCase()).collect(Collectors.toList());
簡化規則如下:
- 絕大部分情況下,編譯器都可以從上下文推算出參數類型,因此可以省略
- 當參數只有一個,可以省略小括號
- 當表達式只包含一條語句時,可以省略大括號、returned和語句結尾的分號
在 Lambda 眼中,外部的變量也是可以訪問的,只是這個變量默認必須是 final ,即便你自己沒有加上,編譯器也會自動幫你加上,所以變大時內部訪問外部變量必須是不可變的(只是引用不可變);還有一個東西就是 this ,this 在 lambda 指代的不是表達式生產的那個 SAM(Simple Abstract Method)對象,而是申明她的外部對象。
?
4、方法引用(Method reference)和構造器引用(Construct reference)
- objectName::instanceMethod ?
- ClassName::staticMethod
例子:System.out::println ? 等同于 ? x -> System.out.println(x)
?????????? Math::max ? ? ? ? ? ? ? ?等同于 ?(x,y) -> Math.max(x,y)
前兩種方法類似,等同于把 Lambda 表達式的參數當成實例方法或者靜態方法的參數來調用,下面再來看看第三種情況 - ClassName::instanceMethod
例子:String.toLowerCase ??等同于 ? x -> x.toLowerCase()
等同于把 Lambda 表達式的第一個參數當成 實例方法的目標對象,其余參數當成該方法的參數來調用。 - ClassName::new
例子:BigDecimal::new ? ?等同于 ? x -> new BigDecimal(x)
等同于把 Lambda 的參數當成構造器的參數來調用。
5、Stream
Stream 堪稱 Lambda 的好基友,他們兩個配合起來使用才能快快樂樂。
-
Stream 是元素的集合,這點讓 Stream 看起來有些類似 iterator
- 可以支持順序和并行的對原 Stream 進行匯聚的操作
//Lists是Guava中的一個工具類 List<Integer> nums = Lists.newArrayList(1,null,3,4,null,6);
?
?
?
?
?
5.1 創建 Stream
// of() 有兩個,變長參數和單一參數 Stream<Integer> integerStream = Stream.of(1, 2, 3, 5); Stream<String> stringStream = Stream.of("taobao"); // 匿名類的寫法 Stream.generate(new Supplier<Double>() {@Overridepublic Double get() {return Math.random();} }); // Lambda 寫法 Stream.generate(() -> Math.random); // 函數引用寫法 Stream.generate(Math::random); // generate() 生產一個無限長度的Stream // iterate() 也是生產了一個無限長度的 Stream ,其是重復調用用戶給定的種子值類獲取元素,seed f(seed) f(f(seed)) ...... Stream.iterate(1, item -> item + 1).limit(10).forEach(System.out::println);
除了上面 Stream 的靜態工廠創建方法之外,就是下面的 Collection 接口的默認方法。
1 public interface Collection<E> extends Iterable<E> { 2 //其他方法省略 3 default Stream<E> stream() { 4 return StreamSupport.stream(spliterator(), false); 5 } 6 }
5.2? 轉換 Stream
顧名思義,就是通過一定的法則將原來的 Stream 轉換成一個新的 Stream,下面列舉一些常用的轉換方法來加深理解,一圖勝千言
?
?
?
從以上圖中就可以看出來幾個常用的 轉換函數的功能,下面我們將大聲的喊出我們的口號“在一起!”
1 List<Integer> nums = Lists.newArrayList(1,1,null,2,3,4,null,5,6,7,8,9,10); 2 System.out.println(“sum is:”+nums.stream().filter(num -> num != null). 3 distinct().mapToInt(num -> num * 2). 4 peek(System.out::println).skip(2).limit(4).sum());
5.3 匯聚(Reduce) Stream
匯聚,也稱為折疊,接受一個元素序列為輸入,反復使用某個合并操作,把序列中的元素合并成一個匯總的結果,下面會分兩部分來介紹匯聚行為。
5.3.1 可變匯聚
把輸入元素累計到一個可變的容器中,比如 Collection 或者 StringBuilder。
5.3.2 其他匯聚
除了可變匯聚之外的其余匯聚,一般不是通過反復修改某個可變對象,而是通過把前一次的匯聚結果當成下一次的入參,反復如此,比如reduce、count、allMatch
?
?