Lambda 表達式的結(jié)構(gòu)
讓我們了解一下 Lambda 表達式的結(jié)構(gòu)蛔垢。
一個 Lambda 表達式可以有零個或多個參數(shù)
參數(shù)的類型既可以明確聲明,也可以根據(jù)上下文來推斷掘而。例如:(int a)與(a)效果相同
所有參數(shù)需包含在圓括號內(nèi)挟冠,參數(shù)之間用逗號相隔。例如:(a, b)或(int a, int b)或(String a, int b, float c)
空圓括號代表參數(shù)集為空袍睡。例如:() -> 42
當(dāng)只有一個參數(shù)知染,且其類型可推導(dǎo)時,圓括號()可省略斑胜。例如:a -> return a*a
Lambda 表達式的主體可包含零條或多條語句
如果 Lambda 表達式的主體只有一條語句控淡,花括號{}可省略。匿名函數(shù)的返回類型與該主體表達式一致
如果 Lambda 表達式的主體包含一條以上語句止潘,則表達式必須包含在花括號{}中(形成代碼塊)掺炭。匿名函數(shù)的返回類型與代碼塊的返回類型一致,若沒有返回則為空
什么是函數(shù)式接口
在 Java 中凭戴,Marker(標(biāo)記)類型的接口是一種沒有方法或?qū)傩月暶鞯慕涌诮ǎ唵蔚卣f,marker 接口是空接口。相似地勋篓,函數(shù)式接口是只包含一個抽象方法聲明的接口吧享。
java.lang.Runnable就是一種函數(shù)式接口,在 Runnable 接口中只聲明了一個方法void run()譬嚣,相似地,ActionListener 接口也是一種函數(shù)式接口钞它,我們使用匿名內(nèi)部類來實例化函數(shù)式接口的對象拜银,有了 Lambda 表達式,這一方式可以得到簡化遭垛。
每個 Lambda 表達式都能隱式地賦值給函數(shù)式接口尼桶,例如,我們可以通過 Lambda 表達式創(chuàng)建 Runnable 接口的引用锯仪。
Runnable r = () -> System.out.println("hello world");
當(dāng)不指明函數(shù)式接口時泵督,編譯器會自動解釋這種轉(zhuǎn)化:
new Thread(
? () -> System.out.println("hello world")
).start();
因此,在上面的代碼中庶喜,編譯器會自動推斷:根據(jù)線程類的構(gòu)造函數(shù)簽名public Thread(Runnable r) { }小腊,將該 Lambda 表達式賦給 Runnable 接口。
以下是一些 Lambda 表達式及其函數(shù)式接口:?
Consumer? c = (int x) -> { System.out.println(x) };
BiConsumer b = (Integer x, String y) -> System.out.println(x + " : " + y);
Predicate p = (String s) -> { s == null };
@FunctionalInterface是 Java 8 新加入的一種接口久窟,用于指明該接口類型聲明是根據(jù) Java 語言規(guī)范定義的函數(shù)式接口秩冈。Java 8 還聲明了一些 Lambda 表達式可以使用的函數(shù)式接口,當(dāng)你注釋的接口不是有效的函數(shù)式接口時斥扛,可以使用 @FunctionalInterface 解決編譯層面的錯誤入问。
以下是一種自定義的函數(shù)式接口:
? ? @FunctionalInterface
? ? public interface WorkerInterface {
? public void doSomeWork();
}
根據(jù)定義,函數(shù)式接口只能有一個抽象方法稀颁,如果你嘗試添加第二個抽象方法芬失,將拋出編譯時錯誤。例如:
@FunctionalInterface
public interface WorkerInterface {
? ? public void doSomeWork();
? ? public void doSomeMoreWork();
}
錯誤:
Unexpected @FunctionalInterface annotation
? ? @FunctionalInterface ^ WorkerInterface is not a functional interface multiple
? ? non-overriding abstract methods found in interface WorkerInterface 1 error
函數(shù)式接口定義好后匾灶,我們可以在 API 中使用它棱烂,同時利用 Lambda 表達式。例如:
//定義一個函數(shù)式接口@FunctionalInterfacepublic interface WorkerInterface {? public void doSomeWork();}public class WorkerInterfaceTest {public static void execute(WorkerInterface worker) {? ? worker.doSomeWork();}public static void main(String [] args) {? ? //invoke doSomeWork using Annonymous class? ? execute(new WorkerInterface() {? ? ? ? @Override? ? ? ? public void doSomeWork() {? ? ? ? ? ? System.out.println("Worker invoked using Anonymous class");? ? ? ? }? ? });? ? //invoke doSomeWork using Lambdaexpression? ? execute( () -> System.out.println("Worker invoked using Lambda expression") );}}
輸出:
Worker invoked using Anonymous class
Worker invoked using Lambda expression
這上面的例子里粘昨,我們創(chuàng)建了自定義的函數(shù)式接口并與 Lambda 表達式一起使用垢啼。execute() 方法現(xiàn)在可以將 Lambda 表達式作為參數(shù)。
Lambda 表達式舉例
學(xué)習(xí) Lambda 表達式的最好方式是學(xué)習(xí)例子张肾。
線程可以通過以下方法初始化:
//舊方法:
new Thread(new Runnable() {
@Override
public void run() {
? ? System.out.println("Hello from thread");
}
}).start();
//新方法:
new Thread(
() -> System.out.println("Hello from thread")
).start();
以下代碼的作用是打印出給定數(shù)組中的所有元素芭析。注意,使用 Lambda 表達式的方法不止一種吞瞪。在下面的例子中馁启,我們先是用常用的箭頭語法創(chuàng)建 Lambda 表達式,之后,使用 Java 8 全新的雙冒號(::)操作符將一個常規(guī)方法轉(zhuǎn)化為 Lambda 表達式:
//Old way:
List list = Arrays.asList(1, 2, 3, 4, 5, 6, 7);
for(Integer n: list) {
? System.out.println(n);
}
//New way:
List list = Arrays.asList(1, 2, 3, 4, 5, 6, 7);
list.forEach(n -> System.out.println(n));
//or we can use :: double colon operator in Java 8
list.forEach(System.out::println);
下面的例子使用 Lambda 表達式打印數(shù)值中每個元素的平方惯疙,注意我們使用了 .stream() 方法將常規(guī)數(shù)組轉(zhuǎn)化為流翠勉。Java 8 增加了一些超棒的流 APIs。java.util.stream.Stream接口包含許多有用的方法霉颠,能結(jié)合 Lambda 表達式產(chǎn)生神奇的效果对碌。我們將 Lambda 表達式x -> x*x傳給 map() 方法,該方法會作用于流中的所有元素蒿偎。之后朽们,我們使用 forEach 方法打印數(shù)據(jù)中的所有元素:
//Old way:
List list = Arrays.asList(1,2,3,4,5,6,7);
for(Integer n : list) {
? ? int x = n * n;
? ? System.out.println(x);
}
//New way:
List list = Arrays.asList(1,2,3,4,5,6,7);
list.stream().map((x) -> x*x).forEach(System.out::println);
下面的例子會計算給定數(shù)值中每個元素平方后的總和。請注意诉位,Lambda 表達式只用一條語句就能達到此功能骑脱,這也是 MapReduce 的一個初級例子。我們使用 map() 給每個元素求平方苍糠,再使用 reduce() 將所有元素計入一個數(shù)值:
//Old way:
List list = Arrays.asList(1,2,3,4,5,6,7);
int sum = 0;
for(Integer n : list) {
? ? int x = n * n;
? ? sum = sum + x;
}
System.out.println(sum);
//New way:
List list = Arrays.asList(1,2,3,4,5,6,7);
int sum = list.stream().map(x -> x*x).reduce((x,y) -> x + y).get();
System.out.println(sum);
Lambda 表達式與匿名類的區(qū)別
使用匿名類與 Lambda 表達式的一大區(qū)別在于關(guān)鍵詞的使用叁丧。對于匿名類,關(guān)鍵詞this解讀為匿名類岳瞭,而對于 Lambda 表達式拥娄,關(guān)鍵詞this解讀為寫就 Lambda 的外部類。