前言
在JDK8和ES6的語言發展中,在Java的lambda表達式和JavaScript的箭頭函數這兩者有着千絲萬縷的聯系;本次試圖通過這篇文章弄懂上面的兩個“語法糖”。
簡介
Lambda 表達式來源於 C# 5.0,但又不太確定,於是查了下 百度百科:Lambda表達式,仍然沒有得到明確的答案,所以懶得去糾結這個問題了。
箭頭函數(arrow function),就是C#中的lambda表達式,據說Java8也把它加入了。但不管怎樣,JS正在從其它語言吸納優秀的特性(比如yield, class, 默認參數等等),且不論這些特性好壞,這件事本身就是極好的(至少我們正在使用的是一個充滿活力的工具)
只是Java用->
箭頭,C#用的箭頭與JS一樣:=>
,這個箭頭叫“lambda運算符”,行話讀作”goes to”
lambda表達式(箭頭函數)據說是定義函數最簡潔的方法,語法上幾乎沒有冗余成分了。因為JS弱類型的特點,JS中的lambda表達式要比C#和Java中的更簡潔(少了參數類型聲明)
一句話,箭頭函數就是lambda表達式,提供了更簡潔的function定義方式
lambda語法
什么是lambda?
這里先給簡要的定義:將匿名函數復制給變量的簡寫方式的函數稱為 lambda 表達式。
1、java的場景中:把“一塊代碼”賦給一個Java變量
這個是簡化過程:
2、JS中同理也是比較簡單的
var fun1 = funcation(int x,int y){
return (x+y);
}
簡寫如下:
var fun1 = (x,y)=>x+y;
lambda的語法定義
這里由於Java一切皆對象的原因,暫時先介紹JavaScript的定義,但Java基本雷同
JavaScript的箭頭函數
Lambda 表達式的主要形式是如下定義,符號的左側是參數,右側是表達式或語句塊。
(參數列表) => { 語句塊 }
當“語句塊”只有一條語句的時候,可以省略大括號,就成了
(參數列表) => 語句
注意: Lambda 表達式一般是作為參數或者值使用,所以根據使用的上下文,大部分情況下編譯器可以推斷出 Lambda 表達式的參數類型;Lambda 表達式的參數通常是省略類型的
Java的lambda表達式
Lambda 表達式的作用其實就是匿名方法,而 Java 中並沒有匿名方法這一語法。不過 Java 中有匿名對象,當你直接 new 一個接口並實現接口方法的時候,Java 編譯器實際是產生了一個類(匿名類)來實現這個接口,然后再返回這個類的一個實例,也就是匿名對象;Lambda表達式本身就是一個接口的實現。
這種只有一個接口函數需要被實現的接口類型,我們叫它”函數式接口“。為了避免后來的人在這個接口中增加接口函數導致其有多個接口函數需要被實現,變成"非函數接口”,我們可以在這個上面加上一個聲明@FunctionalInterface, 這樣別人就無法在里面添加新的接口函數了:
下面是一個完整的案例:
@FunctionalInterface
public 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 Lambda expression
execute( () -> System.out.println("Worker invoked using Lambda expression") );
}
}
lambda的常見使用場景
上面通過兩個簡單的案例演示了lambda,現在總結一下在Java和JavaScript常見的用法和使用常見。
java中常見的使用場景
1. 何時用?
JAVA8中就提供了這種“函數式編程”的方法 —— lambda表達式,供我們來更加簡明扼要的實現內部匿名類的功能。
函數式接口:Functional Interface.
定義的一個接口,接口里面必須 有且只有一個抽象方法 ,這樣的接口就成為函數式接口。
在可以使用lambda表達式的地方,方法聲明時必須包含一個函數式的接口。
簡單的說,凡是(java8以上)函數式接口都可以盡量使用lambda表達式,注意:如果我們提供的這個接口包含一個以上的Abstract Method,那么使用lambda表達式則會報錯。建議定義的接口加上@FunctionalInterface
注解。
2. 怎么用?
根據之前的思路;只要找到Java中函數式接口的皆可以放出lambda表達式
的大招。
-
JDK 8之前已有的函數式接口
java.lang.Runnable
java.util.concurrent.Callable
java.security.PrivilegedAction
java.util.Comparator
java.io.FileFilter
java.nio.file.PathMatcher
java.lang.reflect.InvocationHandler
java.beans.PropertyChangeListener
java.awt.event.ActionListener
javax.swing.event.ChangeListener -
Java SE 8中增加了一個新的包:java.util.function,它里面包含了常用的函數式接口:
補充: 關於函數式接口的詳細總結
1)Java8中Iterable的foreach(Comsumer action)的函數式接口
2)Java8中Iteror的forEachRemaining(Comsumer action)的函數式接口
3)Java8中函數式接口Predicate;Collections的removeIf(Predicate filter)
...其實還有很多,就不一一列舉了,其實根據規則就很容易了加上編譯器優化,其實寫出lambda其實也是很容易。
JavaScript的箭頭函數常用場景:
由於JavaScript基於函數編程,lambda表達式非常靈活常用,基本上對於參數的簡單操作都可以使用箭頭函數完成,這里可以告訴你不要試圖濫用。
- 箭頭函數適合於無復雜邏輯或者無副作用的純函數場景下,例如用在map、reduce、filter的回調函數定義中;
- 不要在最外層定義箭頭函數,因為在函數內部操作this會很容易污染全局作用域。最起碼在箭頭函數外部包一層普通函數,將this控制在可見的范圍內;
- 如開頭所述,箭頭函數最吸引人的地方是簡潔。在有多層函數嵌套的情況下,箭頭函數的簡潔性並沒有很大的提升,反而影響了函數的作用范圍的識別度,這種情況不建議使用箭頭函數。
參考資料