[toc]
前言
在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控制在可见的范围内;
- 如开头所述,箭头函数最吸引人的地方是简洁。在有多层函数嵌套的情况下,箭头函数的简洁性并没有很大的提升,反而影响了函数的作用范围的识别度,这种情况不建议使用箭头函数。
参考资料