成人免费xxxxx在线视频软件_久久精品久久久_亚洲国产精品久久久_天天色天天色_亚洲人成一区_欧美一级欧美三级在线观看

原來 Lambda 表達式是這樣寫的

開發(fā) 后端
Lambda 表達式非常方便,在項目中一般在 stream 編程中用的比較多。

 [[434281]]

低并發(fā)的友友們好,我是閃客。

Lambda 表達式非常方便,在項目中一般在 stream 編程中用的比較多。 

  1. List<Student> studentList = gen();  
  2. Map<String, Student> map = studentList .stream()  
  3.         .collect(Collectors.toMap(Student::getId, a -> a, (a, b) -> a)); 

理解一個 Lambda 表達式就三步:

1. 確認 Lamda 表達式的類型

2. 找到要實現(xiàn)的方法

3. 實現(xiàn)這個方法 

就這三步,沒其他的了。而每一步,都非常非常簡單,以至于我分別展開講一下,你就懂了。

確認 Lambda 表達式的類型

 能用 Lamda 表達式來表示的類型,必須是一個函數(shù)式接口,而函數(shù)式接口,就是只有一個抽象方法的接口。

 我們看下非常熟悉的 Runnable 接口在 JDK 中的樣子就明白了。 

  1. @FunctionalInterface  
  2. public interface Runnable {  
  3.     public abstract void run();  

這就是一個標準的函數(shù)式接口。

因為只有一個抽象方法。而且這個接口上有個注解

@FunctionalInterface

這個僅僅是在編譯期幫你檢查你這個接口是否符合函數(shù)式接口的條件,比如你沒有任何抽象方法,或者有多個抽象方法,編譯是無法通過的。 

  1. // 沒有實現(xiàn)任何抽象方法的接口  
  2. @FunctionalInterface  
  3. public interface MyRunnable {}  
  4. // 編譯后控制臺顯示如下信息  
  5. Error:(3, 1) java:   
  6.   意外的 @FunctionalInterface 注釋  
  7.   MyRunnable 不是函數(shù)接口  
  8.     在 接口 MyRunnable 中找不到抽象方法 

再稍稍復雜一點,Java 8 之后接口中是允許使用默認方法和靜態(tài)方法的,而這些都不算抽象方法,所以也可以加在函數(shù)式接口里。

 看看你可能不太熟悉又有點似曾相識的一個接口。 

  1. @FunctionalInterface  
  2. public interface Consumer<T> {  
  3.     void accept(T t);  
  4.     default Consumer<T> andThen(Consumer<? super T> after) {...}  

看,只有一個抽象方法,還有一個默認方法(方法體的代碼省略了),這個也不影響它是個函數(shù)式接口。再看一個更復雜的,多了靜態(tài)方法,這同樣也是個函數(shù)式接口,因為它仍然只有一個抽象方法。自行體會吧。 

  1. @FunctionalInterface  
  2. public interface Predicate<T> {  
  3.     boolean test(T t);      
  4.     default Predicate<T> and(Predicate<? super T> other) {...}  
  5.     default Predicate<T> negate() {...}  
  6.     default Predicate<T> or(Predicate<? super T> other) {...}  
  7.      static <T> Predicate<T> isEqual(Object targetRef) {...}  
  8.     static <T> Predicate<T> not(Predicate<? super T> target) {...}  

先不用管這些方法都是干嘛的,這些類在 Stream 設計的方法中比比皆是,我們就先記住這么一句話,Lambda 表達式需要的類型為函數(shù)式接口,函數(shù)式接口里只有一個抽象方法,就夠了,以上三個例子都屬于函數(shù)式接口。 

恭喜你,已經(jīng)學會了 Lambda 表達式最難的部分,就是認識函數(shù)式接口。

 找到要實現(xiàn)的方法 

Lambda 表達式就是實現(xiàn)一個方法,什么方法呢?就是剛剛那些函數(shù)式接口中的抽象方法。

 那就太簡單了,因為函數(shù)式接口有且只有一個抽象方法,找到它就行了。我們嘗試把剛剛那幾個函數(shù)式接口的抽象方法找到。 

  1. @FunctionalInterface  
  2. public interface Runnable {  
  3.     public abstract void run();  
  4.  
  5. @FunctionalInterface  
  6. public interface Consumer<T> {  
  7.     void accept(T t);  
  8.     default Consumer<T> andThen(Consumer<? super T> after) {...}  
  9.  
  10. @FunctionalInterface  
  11. public interface Predicate<T> {  
  12.     boolean test(T t);  
  13.     default Predicate<T> and(Predicate<? super T> other) {...}  
  14.     default Predicate<T> negate() {...} 
  15.     default Predicate<T> or(Predicate<? super T> other) {...}  
  16.     static <T> Predicate<T> isEqual(Object targetRef) {...}  
  17.     static <T> Predicate<T> not(Predicate<? super T> target) {...} 

好了,這就找到了,簡單吧!

實現(xiàn)這個方法 

Lambda 表達式就是要實現(xiàn)這個抽象方法,如果不用 Lambda 表達式,你一定知道用匿名類如何去實現(xiàn)吧?比如我們實現(xiàn)剛剛 Predicate 接口的匿名類。 

  1. Predicate<String> predicate = new Predicate<String>() {  
  2.     @Override  
  3.     public boolean test(String s) {  
  4.         return s.length() != 0;  
  5.     }  
  6. }; 

那如果換成 Lambda 表達式呢?就像這樣。 

  1. Predicate<String> predicate =   
  2.     (String s) -> {  
  3.         return s.length() != 0;  
  4.     }; 

 看出來了么?這個 Lambda 語法由三部分組成:

參數(shù)塊:就是前面的 (String s),就是簡單地把要實現(xiàn)的抽象方法的參數(shù)原封不動寫在這。

小箭頭:就是 -> 這個符號。

代碼塊:就是要實現(xiàn)的方法原封不動寫在這。 

不過這樣的寫法你一定不熟悉,連 idea 都不會幫我們簡化成這個奇奇怪怪的樣子,別急,我要變形了!其實是對其進行格式上的簡化。

 首先看參數(shù)快部分,(String s) 里面的類型信息是多余的,因為完全可以由編譯器推導,去掉它。 

  1. Predicate<String> predicate =  
  2.     (s) -> {  
  3.         return s.length() != 0;  
  4.     }; 

 當只有一個參數(shù)時,括號也可以去掉。 

  1. Predicate<String> predicate =   
  2.     s -> {  
  3.         return s.length() != 0;  
  4.     }; 

再看代碼塊部分,方法體中只有一行代碼,可以把花括號和 return 關(guān)鍵字都去掉。 

  1. Predicate<String> p = s -> s.length() != 0; 

這樣看是不是就熟悉點了?

來,再讓我們實現(xiàn)一個 Runnable 接口。 

  1. @FunctionalInterface  
  2. public interface Runnable {  
  3.     public abstract void run();  
  4.  
  5. Runnable r = () -> System.out.println("I am running"); 

你看,這個方法沒有入?yún)?,所以前面括號里的參?shù)就沒有了,這種情況下括號就不能省略。

 通常我們快速新建一個線程并啟動時,是不是像如下的寫法,熟悉吧?

  1. new Thread(() -> System.out.println("I am running")).start(); 

多個入?yún)?/h2>

之前我們只嘗試了一個入?yún)?,接下來我們看看多個入?yún)⒌摹?nbsp;

  1. @FunctionalInterface  
  2. public interface BiConsumer<T, U> {  
  3.     void accept(T t, U u);  
  4.     // default methods removed  

然后看看一個用法,是不是一目了然。 

  1. BiConsumer<Random, Integer> randomNumberPrinter =   
  2.         (random, number) -> {  
  3.             for (int i = 0; i < number; i++) {  
  4.                 System.out.println("next random = " + random.nextInt());  
  5.             } 
  6.         };     
  7.  randomNumberPrinter.accept(new Random(314L), 5)); 

剛剛只是多個入?yún)ⅲ俏覀冊偌觽€返回值。 

  1. @FunctionalInterface  
  2. public interface BiFunction<T, U, R> {  
  3.     R apply(T t, U u);  
  4.     // default methods removed 
  5.   
  6. // 看個例子  
  7. BiFunction<String, String, Integer> findWordInSentence =  
  8.     (word, sentence) -> sentence.indexOf(word); 

發(fā)現(xiàn)規(guī)律了沒

 OK,看了這么多例子,不知道你發(fā)現(xiàn)規(guī)律了沒?

 其實函數(shù)式接口里那個抽象方法,無非就是入?yún)⒌膫€數(shù),以及返回值的類型。入?yún)⒌膫€數(shù)可以是一個或者兩個,返回值可以是 void,或者 boolean,或者一個類型。那這些種情況的排列組合,就是 JDK 給我們提供的java.util.function包下的類。

 BiConsumer

BiFunction

BinaryOperator

BiPredicate

BooleanSupplier

Consumer

DoubleBinaryOperator

DoubleConsumer

DoubleFunction

DoublePredicate

DoubleSupplier

DoubleToIntFunction

DoubleToLongFunction

DoubleUnaryOperator

Function

IntBinaryOperator

IntConsumer

IntFunction

IntPredicate

IntSupplier

IntToDoubleFunction

IntToLongFunction

IntUnaryOperator

LongBinaryOperator

LongConsumer

LongFunction

LongPredicate

LongSupplier

LongToDoubleFunction

LongToIntFunction

LongUnaryOperator

ObjDoubleConsumer

ObjIntConsumer

ObjLongConsumer

Predicate

Supplier

ToDoubleBiFunction

ToDoubleFunction

ToIntBiFunction

ToIntFunction

ToLongBiFunction

ToLongFunction

UnaryOperator

 別看暈了,我們分分類就好了??梢宰⒁獾胶芏囝惽熬Y是 Int,Long,Double 之類的,這其實是指定了入?yún)⒌奶囟愋停辉偈且粋€可以由用戶自定義的泛型,比如說 DoubleFunction。 

  1. @FunctionalInterface  
  2. public interface DoubleFunction<R> {  
  3.     R apply(double value);  

這完全可以由更自由的函數(shù)式接口 Function 來實現(xiàn)。 

  1. @FunctionalInterface  
  2. public interface Function<T, R> {  
  3.     R apply(T t);  

那我們不妨先把這些特定類型的函數(shù)式接口去掉(我還偷偷去掉了 XXXOperator 的幾個類,因為它們都是繼承了別的函數(shù)式接口),然后再排排序,看看還剩點啥。

 Consumer

Function

Predicate 

BiConsumer

BiFunction

BiPredicate 

Supplier 

哇塞,幾乎全沒了,接下來就重點看看這些。這里我就只把類和對應的抽象方法列舉出來

 Consumer  void accept(T t)

Function R apply(T t)

Predicate   boolean test(T t) 

BiConsumer void accept(T t, U u)

BiFunction R apply(T t, U u)

BiPredicate boolean test(T t, U u) 

Supplier T get() 

看出規(guī)律了沒?上面那幾個簡單分類就是: 

supplier:沒有入?yún)ⅲ蟹祷刂怠?/p>

consumer:有入?yún)?,無返回值。

predicate:有入?yún)?,返?boolean 值

function:有入?yún)ⅲ蟹祷刂?nbsp;

然后帶 Bi 前綴的,就是有兩個入?yún)ⅲ粠У木椭挥幸粋€如參。OK,這些已經(jīng)被我們分的一清二楚了,其實就是給我們提供了一個函數(shù)的模板,區(qū)別僅僅是入?yún)⒎祬€數(shù)的排列組合。

 用我們常見的 Stream 編程熟悉一下 

下面這段代碼如果你項目中有用 stream 編程那肯定很熟悉,有一個 Student 的 list,你想把它轉(zhuǎn)換成一個 map,key 是 student 對象的 id,value 就是 student 對象本身。 

  1. List<Student> studentList = gen();  
  2. Map<String, Student> map = studentList .stream()  
  3.         .collect(Collectors.toMap(Student::getId, a -> a, (a, b) -> a)); 

把 Lamda 表達式的部分提取出來。 

  1. Collectors.toMap(Student::getId, a -> a, (a, b) -> a) 

由于我們還沒見過 :: 這種形式,先打回原樣,這里只是讓你預熱一下。 

  1. Collectors.toMap(a -> a.getId(), a -> a, (a, b) -> a) 

為什么它被寫成這個樣子呢?我們看下 Collectors.toMap 這個方法的定義就明白了。 

  1. public static <T, K, U> Collector<T, ?, Map<K,U>> toMap(  
  2.         Function<? super T, ? extends K> keyMapper, 
  3.         Function<? super T, ? extends U> valueMapper,  
  4.         BinaryOperator<U> mergeFunction)   
  5.  
  6.     return toMap(keyMapper, valueMapper, mergeFunction, HashMap::new);  

看,入?yún)⒂腥齻€,分別是Function,F(xiàn)unction,BinaryOperator,其中 BinaryOperator 只是繼承了 BiFunction 并擴展了幾個方法,我們沒有用到,所以不妨就把它當做BiFunction。

 還記得 Function 和 BiFunction 吧? 

Function  R apply(T t)

BiFunction R apply(T t, U u) 

那就很容易理解了。 

第一個參數(shù)a -> a.getId()就是 R apply(T t) 的實現(xiàn),入?yún)⑹?Student 類型的對象 a,返回 a.getId()

 第二個參數(shù)a -> a也是 R apply(T t) 的實現(xiàn),入?yún)⑹?Student 類型的 a,返回 a 本身

 第三個參數(shù)(a, b) -> a是 R apply(T t, U u) 的實現(xiàn),入?yún)⑹荢tudent 類型的 a 和 b,返回是第一個入?yún)?a,Stream 里把它用作當兩個對象 a 和 b 的 key 相同時,value 就取第一個元素 a

 其中第二個參數(shù) a -> a 在 Stream 里表示從 list 轉(zhuǎn)為 map 時的 value 值,就用原來的對象自己,你肯定還見過這樣的寫法。 

  1. Collectors.toMap(a -> a.getId(), Function.identity(), (a, b) -> a) 

為什么可以這樣寫,給你看 Function 類的全貌你就明白了。 

  1. @FunctionalInterface  
  2. public interface Function<T, R> {  
  3.     R apply(T t);   
  4.     ...  
  5.     static <T> Function<T, T> identity() {  
  6.         return t -> t; 
  7.     }  

看到了吧,identity 這個方法,就是幫我們把表達式給實現(xiàn)了,就不用我們自己寫了,其實就是包了個方法。這回知道一個函數(shù)式接口,為什么有好多還要包含一堆默認方法和靜態(tài)方法了吧?就是干這個事用的。

 我們再來試一個,Predicate 里面有這樣一個默認方法。 

  1. @FunctionalInterface  
  2. public interface Predicate<T> {  
  3.     boolean test(T t);  
  4.     default Predicate<T> and(Predicate<? super T> other) {  
  5.         Objects.requireNonNull(other);  
  6.         return (t) -> test(t) && other.test(t);  
  7.     }  

它能干嘛用呢?我來告訴你,如果沒有這個方法,有一段代碼你可能會這樣寫。 

  1. Predicate<String> p =   
  2.     s -> (s != null) &&   
  3.     !s.isEmpty() &&   
  4.     s.length() < 5

如果利用上這個方法,就可以變成如下這種優(yōu)雅形式。 

  1. Predicate<String> nonNull = s -> s != null; 
  2. Predicate<String> nonEmpty = s -> s.isEmpty();  
  3. Predicate<String> sshorterThan5 = s -> s.length() < 5 
  4. Predicate<String> p = nonNull.and(nonEmpty).and(shorterThan5); 

自行體會吧。

 方法引用 

那我們回過頭再看剛剛的 Student::getId 這種寫法。當方法體中只有一個方法調(diào)用時,就可以作這樣的簡化。

 比如這個 a -> a.getId() 就只是對 Student 對象上 getId() 這個方法的調(diào)用,那么就可以寫成 Student::getId 這種形式。

 再看幾個例子 

  1. Function<String, Integer> toLength = s -> s.length();  
  2. Function<String, Integer> toLength = String::length;  
  3. Function<User, String> getName = user -> user.getName();  
  4. Function<String, Integer> toLength = User::getName;  
  5. Consumer<String> printer = s -> System.out.println(s);  
  6. Consumer<String> printer = System.out::println; 

如果是構(gòu)造方法的話,也可以簡化。 

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

總結(jié)

學會理解和寫 Lambda 表達式,別忘了最開始的三步。 

1. 確認 Lamda 表達式的類型

2. 找到要實現(xiàn)的方法

3. 實現(xiàn)這個方法

Lamda 表達式的類型就是函數(shù)式接口,要實現(xiàn)的方法就是函數(shù)式接口里那個唯一的抽象方法,實現(xiàn)這個方法的方式就是參數(shù)塊 + 小箭頭 + 方法體,其中參數(shù)塊和方法體都可以一定程度上簡化它的寫法。

是不是很簡單了!

以上代碼例子,都來源于官方的教程,英語好的同學可以看看,是最科學的 Lamda 表達式教程了。

https://dev.java/learn/tutorial/getting-to-know-the-language/lambda-expressions/lambdas.html

的今天的文章主要就是講怎么寫出 Lambda 表達式,至于原理,之后再說。這里提個引子,你覺得 Lambda 表達式是匿名類的簡化么?按照官方的說法,Lamda 表達式在某些情況下就是匿名類的一種更簡單的寫法,但是從字節(jié)碼層面看,完全不同,這又是怎么回事呢?

帶著這個問題,給自己埋個坑,我們下講說說 Lambda 表達式背后的實現(xiàn)原理。 

【責任編輯:龐桂玉 TEL:(010)68476606】

 

責任編輯:龐桂玉 來源: 程序員書庫
相關(guān)推薦

2012-06-26 10:03:58

JavaJava 8lambda

2009-09-11 09:48:27

Linq Lambda

2009-09-09 13:01:33

LINQ Lambda

2009-09-15 15:18:00

Linq Lambda

2022-12-05 09:31:51

接口lambda表達式

2024-03-25 13:46:12

C#Lambda編程

2009-08-27 09:44:59

C# Lambda表達

2009-09-15 17:30:00

Linq Lambda

2009-09-17 09:44:54

Linq Lambda

2009-09-17 10:40:22

Linq Lambda

2020-10-16 06:40:25

C++匿名函數(shù)

2009-08-27 09:57:50

C# Lambda表達

2009-08-31 17:11:37

Lambda表達式

2009-09-17 09:09:50

Lambda表達式Linq查詢

2009-08-26 16:17:23

C# Lambda表達

2009-07-09 09:51:07

Lambda表達式C#

2013-04-07 15:44:26

Java8Lambda

2009-08-10 09:41:07

.NET Lambda

2009-09-09 17:14:17

Linq lambda

2013-04-10 10:58:19

LambdaC#
點贊
收藏

51CTO技術(shù)棧公眾號

主站蜘蛛池模板: 精品一区二区三区四区五区 | 国产成人午夜高潮毛片 | 91在线第一页 | 日韩高清一区二区 | 国产精品久久久久久久久久久久 | 色婷婷综合久久久久中文一区二区 | 一级毛片,一级毛片 | 自拍偷拍亚洲欧美 | 一区二区蜜桃 | 国内精品视频一区二区三区 | 欧美aⅴ在线观看 | 狠狠色综合网站久久久久久久 | 国产中文字幕在线 | av中文字幕在线观看 | 九九热这里 | 成人三级网址 | 亚洲国产精品成人 | 免费电影av| av在线免费播放 | 亚州综合一区 | 九九在线视频 | 91精品久久久 | 色爱区综合 | 99精品久久久久 | 国产高清一二三区 | 午夜不卡一区二区 | 亚洲精品第一页 | 日韩精品在线免费观看 | 成年女人免费v片 | www.887色视频免费 | 99视频免费看 | 久热国产在线 | 中文字幕av在线一二三区 | a级片在线 | 国产探花在线观看视频 | 亚洲第一网站 | 成人在线视频一区二区三区 | 精品国产欧美在线 | 国产乱一区二区三区视频 | 久久这里只有精品首页 | 欧美精品在线免费 |