摘要:上下文比如接受它傳遞的方法的參數(shù),或接受它的值的局部變量中表達(dá)式需要的類(lèi)型稱(chēng)為目標(biāo)類(lèi)型。但局部變量必須顯式聲明為,或事實(shí)上是。換句話說(shuō),表達(dá)式只能捕獲指派給它們的局部變量一次。注捕獲實(shí)例變量可以被看作捕獲最終局部變量。
簡(jiǎn)介 概念
Lambda 表達(dá)式可以理解為簡(jiǎn)潔地表示可傳遞的匿名函數(shù)的一種方式:它沒(méi)有名稱(chēng),但它有參數(shù)列表、函數(shù)主體、返回類(lèi)型,可能還有一個(gè)可以拋出的異常列表。
匿名:它不像普通方法那樣有一個(gè)明確的名稱(chēng);
函數(shù):Lambda 表達(dá)式是函數(shù)是因?yàn)樗幌穹椒菢訉儆谀硞€(gè)特定的類(lèi),但和方法一樣,Lambda 有參數(shù)列表、函數(shù)主體、返回類(lèi)型,還可能有可以拋出的異常列表;
傳遞:Lambda 表達(dá)式可以作為參數(shù)傳遞給方法或存儲(chǔ)在變量中;
簡(jiǎn)潔:無(wú)需像匿名類(lèi)那樣寫(xiě)很多模板代碼;
組成Lambda 表達(dá)式由參數(shù)列表、箭頭和 Lambda 主體組成。
(Apple o1, Apple o2) -> Integer.valueOf(o1.getWeight()).compareTo(Integer.valueOf(o2.getWeight()))
參數(shù)列表:這里采用了 Comparator 中 compareTo 方法的參數(shù);
箭頭:箭頭把參數(shù)列表和 Lambda 主體分開(kāi);
Lambda 主體:表達(dá)式就是 Lambda 的返回值;
表達(dá)式Java8中有效的 Lambda 表達(dá)式如下:
Lambda 表達(dá)式 | 含義 |
---|---|
(String s) -> s.length() | 表達(dá)式具有一個(gè) String 類(lèi)型的參數(shù)并返回一個(gè) int。 Lambda 沒(méi)有 return 語(yǔ)句,因?yàn)橐呀?jīng)隱含的 return,可以顯示調(diào)用 return。 |
(Apple a) -> a.getWeight() > 150 | 表達(dá)式有一個(gè) Apple 類(lèi)型的參數(shù)并返回一個(gè) boolean 值 |
(int x, int y) -> { System.out.printn("Result"); System.out.printn(x + y)} |
表達(dá)式具有兩個(gè) int 類(lèi)型的參數(shù)而沒(méi)有返回值(void返回),Lambda 表達(dá)式可以包含多行語(yǔ)句,但必須要使用大括號(hào)包起來(lái)。 |
() -> 42 | 表達(dá)式?jīng)]有參數(shù),返回一個(gè) int 類(lèi)型的值。 |
(Apple o1, Apple o2) -> Integer.valueOf(o1.getWeight()) .compareTo (Integer.valueOf(o2.getWeight())) |
表達(dá)式具有兩個(gè) Apple 類(lèi)型的參數(shù),返回一個(gè) int 比較重要。 |
下面提供一些 Lambda 表達(dá)式的使用案例:
使用案例 | Lambda 示例 |
---|---|
布爾表達(dá)式 | (List |
創(chuàng)建對(duì)象 | () -> new Apple(10) |
消費(fèi)對(duì)象 | (Apple a) -> { System.out.println(a.getWeight) } |
從一個(gè)對(duì)象中選擇/抽取 | (String s) -> s.lenght() |
組合兩個(gè)值 | (int a, int b) -> a * b |
比較兩個(gè)對(duì)象 |
`(Apple o1, Apple o2) -> Integer.valueOf(o1.getWeight()) .compareTo(Integer.valueOf(o2.getWeight())) |
到底在哪里可以使用 Lambda 呢?你可以在函數(shù)式接口上使用 Lambda 表達(dá)式。
函數(shù)式接口函數(shù)式接口就是只定義一個(gè)抽象方法的接口,比如 Java API 中的 Predicate、Comparator 和 Runnable 等。
public interface Predicate{ boolean test(T t); } public interface Comparator { int compare(T o1, T o2); } public interface Runnable { void run(); }
用函數(shù)式接口可以干什么呢?Lambda 表達(dá)式允許你直接以?xún)?nèi)聯(lián)的形式為函數(shù)式接口的抽象方法提供實(shí)現(xiàn),并把整個(gè)表達(dá)式作為函數(shù)式接口的實(shí)例(具體說(shuō)來(lái),是函數(shù)式接口一個(gè)具體實(shí)現(xiàn) 的實(shí)例)。你用匿名內(nèi)部類(lèi)也可以完成同樣的事情,只不過(guò)比較笨拙:需要提供一個(gè)實(shí)現(xiàn),然后 再直接內(nèi)聯(lián)將它實(shí)例化。下面的代碼是有效的,因?yàn)镽unnable是一個(gè)只定義了一個(gè)抽象方法run 的函數(shù)式接口:
//使用Lambda Runnable r1 = () -> System.out.println("Hello World 1"); //匿名類(lèi) Runnable r2 = new Runnable(){ public void run(){ System.out.println("Hello World 2"); } }; public static void process(Runnable r){ r.run(); } process(r1); //打印 "Hello World 1" process(r2); //打印 "Hello World 2" //利用直接傳遞的 Lambda 打印 "Hello World 3" process(() -> System.out.println("Hello World 3"));函數(shù)描述符
函數(shù)式接口的抽象方法的簽名基本上就是 Lambda 表達(dá)式的簽名。我們將這種抽象方法叫作函數(shù)描述符。例如,Runnable 接口可以看作一個(gè)什么也不接受什么也不返回(void)的函數(shù)的簽名,因?yàn)樗挥幸粋€(gè)叫作 run 的抽象方法,這個(gè)方法什么也不接受,什么也不返回(void)。
Lambda 實(shí)踐讓我們通過(guò)一個(gè)例子,看看在實(shí)踐中如何利用Lambda和行為參數(shù)化來(lái)讓代碼更為靈活,更為簡(jiǎn)潔。
資源處理(例如處理文件或數(shù)據(jù)庫(kù))時(shí)一個(gè)常見(jiàn)的模式就是打開(kāi)一個(gè)資源,做一些處理,然后關(guān)閉資源。這個(gè)設(shè)置和清理階段總是很類(lèi)似,并且會(huì)圍繞著執(zhí)行處理的那些重要代碼。這就是所謂的環(huán)繞執(zhí)行(execute around)模式。
例如,在以下代碼中,高亮顯示的BufferedReader reader = new BufferedReader(new FileReader("data.txt"))就是從一個(gè)文件中讀取一行所需的模板代碼(注意你使用了Java 7中的帶資源的try語(yǔ)句,它已經(jīng)簡(jiǎn)化了代碼,因?yàn)槟悴恍枰@式地關(guān)閉資源了)。
public static String processFile() throws IOException { try (BufferedReader reader = new BufferedReader(new FileReader("data.txt"))) { return reader.readLine(); } }第1步:行為參數(shù)化
現(xiàn)在上述代碼是有局限的。你只能讀文件的第一行。如果你想要返回頭兩行,甚至是返回使用最頻繁的詞, 該怎么辦呢?在理想的情況下, 你要重用執(zhí)行設(shè)置和清理的代碼, 并告訴 processFile 方法對(duì)文件執(zhí)行不同的操作。是的,你需要把 processFile 的行為參數(shù)化,你需要一種方法把行為傳遞給 processFile , 以便它可以利用 BufferedReader執(zhí)行不同的行為。
傳遞行為正是 Lambda 的優(yōu)勢(shì)。那要是想一次讀兩行,這個(gè)新的processFile方法看起來(lái)又該是什么樣的呢? 你需要一個(gè)接收BufferedReader并返回String的Lambda。例如,下面就是從 BufferedReader 中打印兩行的寫(xiě)法:
String result = processFile((BufferedReader r) -> r.readLine() +r.readLine());第2步:函數(shù)式接口傳遞行為
Lambda 僅可用于上下文是函數(shù)式接口的情況。你需要?jiǎng)?chuàng)建一個(gè)能匹配 BufferedReader -> String,還可以拋出 IOException 異常的接口。讓我們把這一接口稱(chēng)為 BufferedReaderProcessor。
@FunctionalInterface public interface BufferedReaderProcessor { String process(BufferedReader reader) throws IOException; }第3步:執(zhí)行一個(gè)行為
任何BufferedReader -> String形式的 Lambda 都可以作為參數(shù)來(lái)傳遞,因?yàn)樗鼈兎?BufferedReaderProcessor 接口中定義的 process 方法的簽名?,F(xiàn)在只需要編寫(xiě)一種方法在 processFile主體內(nèi)執(zhí)行 Lambda 所代表的代碼。
public static String processFile(BufferedReaderProcessor processor) throws IOException { try (BufferedReader reader = new BufferedReader(new FileReader("data.txt"))) { return processor.process(reader); //處理 BufferedReader 對(duì)象 } }第4步:傳遞 Lambda
現(xiàn)在就可以通過(guò)傳遞不同的 Lambda 重用 processFile 方法,并以不同的方式處理文件了。
//打印一行 String result = processFile((BufferedReader r) -> r.readLine()); System.out.println(result); //打印2行 result = processFile((BufferedReader r) -> r.readLine() +r.readLine());使用函數(shù)式接口
Java 8的庫(kù)幫你在java.util.function包中引入了幾個(gè)新的函數(shù)式接口。我們接下來(lái)介紹 Predicate、Consumer和Function 三種函數(shù)式接口。
Predicatejava.util.function.Predicate
@FunctionalInterface public interface PredicateConsumer{ boolean test(T t); } public static List filter(List list, Predicate p) { List results = new ArrayList<>(); for(T s: list){ if(p.test(s)){ results.add(s); } } return results; } Predicate nonEmptyStringPredicate = (String s) -> !s.isEmpty(); List nonEmpty = filter(listOfStrings, nonEmptyStringPredicate);
java.util.function.Consumer
@FunctionalInterface public interface ConsumerFunction{ void accept(T t); } public static void forEach(List list, Consumer c){ for(T i: list){ c.accept(i); } } forEach(Arrays.asList(1,2,3,4,5), (Integer i) -> System.out.println(i) );
java.util.function.Function
泛型 T 的對(duì)象,并返回一個(gè)泛型 R 的對(duì)象。如果你需要定義一個(gè)Lambda,將輸入對(duì)象的信息映射到輸出,就可以使用這個(gè)接口(比如提取蘋(píng)果的重量,或把字符串映射為它的長(zhǎng)度)。在下面的代碼中,我們向你展示如何利用它來(lái)創(chuàng)建一個(gè)map方法,以將一個(gè)String列表映射到包含每個(gè) String長(zhǎng)度的Integer列表。
@FunctionalInterface public interface Function原始類(lèi)型特化{ R apply(T t); } public static List map(List list, Function f) { List result = new ArrayList<>(); for(T s: list) { result.add(f.apply(s)); } return result; } // [7, 2, 6] List l = map( Arrays.asList("lambdas","in","action"), (String s) -> s.length() );
Java類(lèi)型要么是引用類(lèi)型(比如Byte、Integer、Object、List),要么是原始類(lèi)型(比如int、double、byte、char)。但是泛型(比如Consumer
Java 8為我們前面所說(shuō)的函數(shù)式接口帶來(lái)了一個(gè)專(zhuān)門(mén)的版本,以便在輸入和輸出都是原始類(lèi)型時(shí)避免自動(dòng)裝箱的操作。比如,使用 IntPredicate 就避免了對(duì)值 1000 進(jìn)行裝箱操作,但要是用 Predicate
下表中列出 Java 8 中常用的函數(shù)式接口:
函數(shù)式接口 | 函數(shù)描述符 | 原始類(lèi)型特化 |
---|---|---|
Predicate |
T -> boolean | IntPredicate,LongPredicate, DoublePredicate |
Consumer |
T -> void | IntConsumer,LongConsumer, DoubleConsumer |
Function |
T -> R | IntFunction |
Supplier |
() -> T | BooleanSupplier,IntSupplier, LongSupplier, DoubleSupplier |
UnaryOperator |
T -> T | IntUnaryOperator, LongUnaryOperator, DoubleUnaryOperator |
BinaryOperator |
(T,T) -> T | IntBinaryOperator, LongBinaryOperator, DoubleBinaryOperator |
BiPredicate |
(L,R) -> boolean | |
BiConsumer |
(T,U) -> R | ObjIntConsumer |
BiFunction |
(T,U) -> R | ToIntBiFunction |
Lambda 的類(lèi)型是從使用 Lambda 的上下文推斷出來(lái)的。上下文(比如接受它傳遞的方法的參數(shù),或接受它的值的局部變量)中 Lambda 表達(dá)式需要的類(lèi)型稱(chēng)為目標(biāo)類(lèi)型。下圖表示了代碼的類(lèi)型檢查過(guò)程:
類(lèi)型檢查過(guò)程可以分解為如下所示:
首先,找出 filter 方法的聲明;
第二,找出目標(biāo)類(lèi)型 Predicate
第三,Predicate
第四,test 方法描述了一個(gè)函數(shù)描述符,它可以接受一個(gè) Apple,并返回一個(gè) boolean。
最后,filter 的任何實(shí)際參數(shù)都必須匹配這個(gè)要求。
同樣的 Lambda,不同的函數(shù)式接口用一個(gè) Lambda 表達(dá)式就可以與不同的函數(shù)式接口聯(lián)系起來(lái),只要它們的抽象方法簽名能夠兼容。比如,前面提到的 Callable 和 PrivilegeAction,這兩個(gè)接口都代表著什么也不接受且返回一個(gè)泛型 T 的函數(shù)。如下代碼所示兩個(gè)賦值時(shí)有效的:
Callablec = () -> 42; PrivilegeAction p = () -> 42;
特殊的void兼容規(guī)則如果一個(gè)Lambda的主體是一個(gè)語(yǔ)句表達(dá)式, 它就和一個(gè)返回void的函數(shù)描述符兼容(當(dāng)然需要參數(shù)列表也兼容)。例如,以下兩行都是合法的,盡管 List 的 add 方法返回了一個(gè) boolean,而不是 Consumer 上下文(T -> void)所要求的void:
//Predicate 返回一個(gè) boolean Predicate類(lèi)型推斷p = s -> list.add(s); //Consumer 返回一個(gè) void Consumer b = s -> list.add(s);
Java編譯器會(huì)從上下文(目標(biāo)類(lèi)型)推斷出用什么函數(shù)式接口來(lái)配合 Lambda 表達(dá)式,這意味著它也可以推斷出適合Lambda 的簽名,因?yàn)楹瘮?shù)描述符可以通過(guò)目標(biāo)類(lèi)型來(lái)得到。這樣做的好處在于,編譯器可以了解Lambda表達(dá)式的參數(shù)類(lèi)型,這樣就可以在Lambda語(yǔ)法中省去標(biāo)注參數(shù)類(lèi)型。
List使用局部變量greenApples = filter(inventory, a -> "green".equals(a.getColor())); //參數(shù)a沒(méi)有顯示類(lèi)型 Comparator c = (Apple a1, Apple a2) -> a1.getWeight().compareTo(a2.getWeight()); //無(wú)類(lèi)型推斷 Comparator c = (a1, a2) -> a1.getWeight().compareTo(a2.getWeight()); //類(lèi)型推斷
Lambda表達(dá)式也允許使用自由變量(不是參數(shù),而是在外層作用域中定義的變量),就像匿名類(lèi)一樣。 它們被稱(chēng)作捕獲Lambda。例如,下面的Lambda捕獲了portNumber變量:
int num = 1337; Runnable r = () -> System.out.println(num);
Lambda可以沒(méi)有限制地捕獲(也就是在其主體中引用)實(shí)例變量和靜態(tài)變量。但局部變量必須顯式聲明為final, 或事實(shí)上是final。換句話說(shuō),Lambda表達(dá)式只能捕獲指派給它們的局部變量一次。(注:捕獲 實(shí)例變量可以被看作捕獲最終局部變量this。) 例如,下面的代碼無(wú)法編譯,因?yàn)閜ortNumber 變量被賦值兩次:
int portNumber = 1337; Runnable r = () -> System.out.println(portNumber); portNumber = 31337; //錯(cuò)誤:Lambda表達(dá)式引用的局 部變量必須是最終的(final) 或事實(shí)上最終的
為什么局部變量有這些限制?第一,實(shí)例變量和局部變量背后的實(shí)現(xiàn)有一 個(gè)關(guān)鍵不同。實(shí)例變量都存儲(chǔ)在堆中,而局部變量則保存在棧上。如果Lambda可以直接訪問(wèn)局部變量,而且Lambda是在一個(gè)線程中使用的,則使用Lambda的線程,可能會(huì)在分配該變量的線程將這個(gè)變量收回之后,去訪問(wèn)該變量。因此,Java在訪問(wèn)自由局部變量時(shí),實(shí)際上是在訪問(wèn)它的副本,而不是訪問(wèn)原始變量。如果局部變量?jī)H僅賦值一次那就沒(méi)有什么區(qū)別了——因此就有了這個(gè)限制。第二,這一限制不鼓勵(lì)你使用改變外部變量的典型命令式編程模式(這種模式會(huì)阻礙很容易做到的并行處理)。
方法引用方法引用讓你可以重復(fù)使用現(xiàn)有的方法定義,并像Lambda一樣傳遞它們。在一些情況下,比起使用 Lambda 表達(dá)式,它們似乎更易讀,感覺(jué)也更自然。下面就是我們借助更新的Java 8 API,用方法引用寫(xiě)的一個(gè)排序的例子:
lists.sort(comparing(Apple::getWeight);如何使用
方法引用可以被看作僅僅調(diào)用特定方法的Lambda的一種快捷寫(xiě)法。它的基本思想是,如果一個(gè)Lambda代表的只是“直接調(diào)用這個(gè)方法”,那最好還是用名稱(chēng)來(lái)調(diào)用它,而不是去描述如何調(diào)用它。事實(shí)上,方法引用就是讓你根據(jù)已有的方法實(shí)現(xiàn)來(lái)創(chuàng)建 Lambda表達(dá)式。但是,顯式地指明方法的名稱(chēng),你的代碼的可讀性會(huì)更好。它是如何工作的呢? 當(dāng)你需要使用方法引用時(shí), 目標(biāo)引用放在分隔符 :: 前, 方法的名稱(chēng)放在后面。 例如, Apple::getWeight就是引用了Apple類(lèi)中定義的方法getWeight。請(qǐng)記住,不需要括號(hào),因?yàn)?你沒(méi)有實(shí)際調(diào)用這個(gè)方法。方法引用就是Lambda表達(dá)式(Apple a) -> a.getWeight()的快捷寫(xiě)法,下表給出了Java 8中方法引用的其他一些例子。
Lambda | 等效的引用方法 |
---|---|
(Apple a) -> a.getWeight() | Apple::getWeight |
() -> Thread.currentThread().dumpStack() | Thread.currentThread()::dumpStack |
(str,i) -> str.substring(i) | String::substring |
(String i) -> System.out.println(s) | System.out::println |
方法引用主要分為三類(lèi):
指向靜態(tài)方法的引用(例如 Integer 的 parseInt 方法,寫(xiě)作 Integer::parseInt)
指向任意類(lèi)型實(shí)例方法的方法引用(例如 String 的 length 方法,寫(xiě)作 String::length)
指向現(xiàn)有對(duì)象的實(shí)例方法的引用(假設(shè)有一個(gè)局部變量 expensiveTransaction 用于存放 Transaction 類(lèi)型的對(duì)象,它支持實(shí)例方法 getValue,那么就可以寫(xiě) expensiveTransaction::getValue)
構(gòu)造函數(shù)引用注意,編譯器會(huì)進(jìn)行一種與Lambda表達(dá)式類(lèi)似的類(lèi)型檢查過(guò)程,來(lái)確定對(duì)于給定的函數(shù) 式接口,這個(gè)方法引用是否有效:方法引用的簽名必須和上下文類(lèi)型匹配。
對(duì)于一個(gè)現(xiàn)有構(gòu)造函數(shù),可以利用它的名稱(chēng)和關(guān)鍵字 new 來(lái)創(chuàng)建它的一個(gè)引用: ClassName::new。它的功能與指向靜態(tài)方法的引用類(lèi)似。
例如,假設(shè)有一個(gè)構(gòu)造函數(shù)沒(méi)有參數(shù)。 它適合 Supplier 的簽名() -> Apple。可以這樣做:
Supplierc1 = Apple::new; //構(gòu)造函數(shù)引用指向默認(rèn)的 Apple() 構(gòu)造函數(shù) Apple a1 = c1.get(); //產(chǎn)生一個(gè)新的對(duì)象 //等價(jià)于: Supplier c1 = () -> new Apple(); //利用默認(rèn)構(gòu)造函數(shù)創(chuàng)建 Apple 的 Lambda 表達(dá)式 Apple a1 = c1.get();
如果你的構(gòu)造函數(shù)的簽名是Apple(Integer weight),那么它就適合 Function 接口的簽名,于是可以這樣寫(xiě):
Functionc2 = Apple::new; //構(gòu)造函數(shù)引用指向 Apple(Integer weight) 構(gòu)造函數(shù) Apple a2 = c2.apple(100); //等價(jià)于: Function c2 = (Integer weight) -> new Apple(weight); Apple a2 = c2.apple(100);
如果你有一個(gè)具有兩個(gè)參數(shù)的構(gòu)造函數(shù)Apple(String color, Integer weight),那么它就適合BiFunction接口的簽名,于是可以這樣寫(xiě):
BiFunctionLambda 和方法引用實(shí)戰(zhàn) 第1步:傳遞代碼c3 = Apple::new; Apple a3 = c23.apple("green", 100); //等價(jià)于: BiFunction c3 = (String color, Integer weight) -> new Apple(color, weight); Apple a3 = c3.apple("green", 100);
Java 8的API已經(jīng)為你提供了一個(gè) List 可用的 sort 方法,那么如何把排序策略傳遞給 sort 方法呢?sort方法的簽名是這樣的:
void sort(Comparator super E> c)
它需要一個(gè) Comparator 對(duì)象來(lái)比較兩個(gè)Apple!這就是在Java中傳遞策略的方式:它們必須包裹在一個(gè)對(duì)象里。我們說(shuō) sort 的行為被參數(shù)化了:傳遞給它的排序策略不同,其行為也會(huì) 不同。
第一個(gè)解決方案可以是這樣的:
public class AppleComparator implements Comparator第2步:使用匿名類(lèi){ @Override public int compare(Apple o1, Apple o2) { return o1.getWeight().compareTo(o2.getWeight()); } } apples.sort(new AppleComparator())
可以使用匿名類(lèi)來(lái)改進(jìn)方案,而不是實(shí)現(xiàn)一個(gè) Comparator 卻只實(shí)例化一次:
apples.sort(new Comparator第3步:使用 Lambda 表達(dá)式() { @Override public int compare(Apple o1, Apple o2) { return o1.getWeight().compareTo(o2.getWeight()); } });
接下來(lái)使用 Lambda 表達(dá)式來(lái)改進(jìn)方案:
apples.sort((Apple a1,Apple a2) -> a1.getWeight().compareTo(a2.getWeight()));
Comparator 具有一個(gè)叫作 comparing 的靜態(tài)輔助方法,它可以接受一個(gè) Function 來(lái)提取 Comparable 鍵值,并生成一個(gè) Comparator 對(duì)象,它可以像下面這樣用(注意你現(xiàn)在傳遞的Lambda只有一 個(gè)參數(shù):Lambda說(shuō)明了如何從蘋(píng)果中提取需要比較的鍵值):
apples.sort(Comparator.comparing(((Apple apple) -> apple.getWeight())));第4步:使用方法引用
方法引用就是替代那些轉(zhuǎn)發(fā)參數(shù)的 Lambda 表達(dá)式的語(yǔ)法糖。可以用方法引 用改進(jìn)方案如下:
apples.sort(Comparator.comparing(Apple::getWeight));復(fù)合 Lambda 表達(dá)式 比較器復(fù)合
逆序:Comparator 接口有一個(gè)默認(rèn)方法 reversed 可以使給定的比較器逆序。
apples.sort(Comparator.comparing(Apple::getWeight).reversed()); //按重量遞減排序
比較器鏈:Comparator 接口的 thenComparing 方法接受一個(gè)函數(shù)作為參數(shù)(就像 comparing方法一樣),如果兩個(gè)對(duì)象用第一個(gè)Comparator比較之后是相等的,就提供第二個(gè) Comparator。
apples.sort(Comparator.comparing(Apple::getWeight).reversed().thenComparing(Apple::getColor)); //按重量遞減排序,一樣重時(shí),按顏色排序謂詞復(fù)合
謂詞接口包括三個(gè)方法:negate、and和or。
//蘋(píng)果不是紅的 Predicate函數(shù)復(fù)合notRedApple = redApple.negate(); //蘋(píng)果是紅色并且重量大于150 Predicate redAndHeavyApple = redApple.and(a -> a.getWeight() > 150); //要么是150g以上的紅蘋(píng)果,要么是綠蘋(píng)果 Predicate redAndHeavyAppleOrGreen = redApple.and(a -> a.getWeight() > 150) .or(a -> "green".equals(a.getColor()));
Function 接口的 andThen 方法Function
Functionf = x -> x + 1; Function g = x -> x * 2; Function h = f.andThen(g); //g(f(x)) int result = h.apply(1); //result = 4
Function 接口的 Compose 方法Function
Function小結(jié)f = x -> x + 1; Function g = x -> x * 2; Function h = f.compose(g); //f(g(x)) int result = h.apply(1); //result = 3
Lambda表達(dá)式可以理解為一種匿名函數(shù):它沒(méi)有名稱(chēng),但有參數(shù)列表、函數(shù)主體、返回 類(lèi)型,可能還有一個(gè)可以拋出的異常的列表。
Lambda表達(dá)式讓你可以簡(jiǎn)潔地傳遞代碼。
函數(shù)式接口就是僅僅聲明了一個(gè)抽象方法的接口。
只有在接受函數(shù)式接口的地方才可以使用Lambda表達(dá)式。
Lambda表達(dá)式允許你直接內(nèi)聯(lián),為函數(shù)式接口的抽象方法提供實(shí)現(xiàn),并且將整個(gè)表達(dá)式作為函數(shù)式接口的一個(gè)實(shí)例。
Java 8自帶一些常用的函數(shù)式接口,放在java.util.function包里,包括Predicate
為了避免裝箱操作,對(duì)Predicate
環(huán)繞執(zhí)行模式(即在方法所必需的代碼中間,你需要執(zhí)行點(diǎn)兒什么操作,比如資源分配 和清理)可以配合 Lambda 提高靈活性和可重用性。
Lambda 表達(dá)式所需要代表的類(lèi)型稱(chēng)為目標(biāo)類(lèi)型。
方法引用讓你重復(fù)使用現(xiàn)有的方法實(shí)現(xiàn)并直接傳遞它們。
Comparator、Predicate和Function等函數(shù)式接口都有幾個(gè)可以用來(lái)結(jié)合 Lambda 表達(dá)式的默認(rèn)方法。
參考資料《Java 8 實(shí)戰(zhàn)》
文章版權(quán)歸作者所有,未經(jīng)允許請(qǐng)勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。
轉(zhuǎn)載請(qǐng)注明本文地址:http://www.ezyhdfw.cn/yun/69922.html
摘要:歐陽(yáng)思海繼承接口后,又加了新的抽象方法,這個(gè)接口就不再是函數(shù)式接口默認(rèn)方法在接口中添加了一個(gè)默認(rèn)方法??偨Y(jié)在這篇文章中,我們講了表達(dá)式方法引用函數(shù)式接口接口中的靜態(tài)方法接口中的默認(rèn)方法的使用。 今天我來(lái)聊聊 Java8 的一些新的特性,確實(shí) Java8 的新特性的出現(xiàn),給開(kāi)發(fā)者帶來(lái)了非常大的便利,可能剛剛開(kāi)始的時(shí)候會(huì)有點(diǎn)不習(xí)慣的這種寫(xiě)法,但是,當(dāng)你真正的熟悉了之后,你一定會(huì)愛(ài)上這些新的...
摘要:接口有一個(gè)方法,可以返回值。在上面的代碼中,就是獲取字符串的長(zhǎng)度,然后將每個(gè)字符串的長(zhǎng)度作為返回值返回。 今天我們還講講Consumer、Supplier、Predicate、Function這幾個(gè)接口的用法,在 Java8 的用法當(dāng)中,這幾個(gè)接口雖然沒(méi)有明目張膽的使用,但是,卻是潤(rùn)物細(xì)無(wú)聲的。為什么這么說(shuō)呢? 這幾個(gè)接口都在 java.util.function 包下的,分別是Con...
摘要:很多語(yǔ)言等從設(shè)計(jì)之初就支持表達(dá)式。注意此時(shí)外部局部變量將自動(dòng)變?yōu)樽鳛榉椒ǚ祷刂道臃祷嘏袛嘧址欠駷榭张袛嘧址欠駷榭战裉礻P(guān)于新特性表達(dá)式就講到這里了,接下來(lái)我會(huì)繼續(xù)講述新特性之函數(shù)式接口。 上一篇文章我們了解了Java8新特性-接口默認(rèn)方法,接下來(lái)我們聊一聊Java8新特性之Lambda表達(dá)式。 Lambda表達(dá)式(也稱(chēng)為閉包),它允許我們將函數(shù)當(dāng)成參數(shù)傳遞給某個(gè)方法,或者把代碼...
摘要:大家好,我是樂(lè)字節(jié)的小樂(lè),上一次我們說(shuō)到了核心特性之函數(shù)式接口,接下來(lái)我們繼續(xù)了解又一核心特性方法引用。方法引用是一種更簡(jiǎn)潔易懂的表達(dá)式。感謝光臨閱讀小樂(lè)的,敬請(qǐng)關(guān)注樂(lè)字節(jié)后續(xù)將繼續(xù)講述等前沿知識(shí)技術(shù)。 大家好,我是樂(lè)字節(jié)的小樂(lè),上一次我們說(shuō)到了Java8核心特性之函數(shù)式接口,接下來(lái)我們繼續(xù)了解Java8又一核心特性——方法引用。 showImg(https://segmentfaul...
摘要:一表達(dá)式匿名內(nèi)部類(lèi)最大的問(wèn)題在于其冗余的語(yǔ)法,比如前面的中五行代碼僅有一行是在執(zhí)行任務(wù)。總結(jié)基于詞法作用域的理念,表達(dá)式不可以掩蓋任何其所在上下文的局部變量。 轉(zhuǎn)載請(qǐng)注明出處:https://zhuanlan.zhihu.com/p/20540175 在介紹Lambda表達(dá)式之前,我們先來(lái)看只有單個(gè)方法的Interface(通常我們稱(chēng)之為回調(diào)接口): public interface...
閱讀 3093·2021-10-19 11:46
閱讀 1042·2021-08-03 14:03
閱讀 3047·2021-06-11 18:08
閱讀 2983·2019-08-29 13:52
閱讀 2896·2019-08-29 12:49
閱讀 573·2019-08-26 13:56
閱讀 990·2019-08-26 13:41
閱讀 906·2019-08-26 13:35