首先我们先了解下 Lambda 表达式名字的由来。实际上这个名字来自微积分数学中的 λ,其涵义是声明为了表达一个函数具体需要什么。更确切的说,它描述了一个数学逻辑系统,通过变量结合和替换来表达计算。所以,基本上我们有 0-n 个输入参数和一个返回值。而在编程语言中,我们也提供了无返回值的 void 支持。
通俗易懂的来讲就是函数式编程,熟悉Python的人对Lambda表达式定然十分了解了,而在java中确实在jdk1.8的时候才推出的。放到java中对比的话,就可以把Lambda表达式看作是java中的匿名内部类。如下
//匿名内部类写法
new Thread(new Runnable() {
@Override
public void run() {
System.out.println("Hello 匿名内部类!");
}
}).start();
//Lambda写法
new Thread(() -> System.out.println("Hello Lambda!")).start();
使用起来很简单,也很简洁,匿名内部类需要6行代码才能完成的事儿,Lambda只需要1行就可以完成。
总的来说,Lambda就是java的语法糖!!!
Lambda也被人叫做函数式编程,那么自然就少不了函数式接口(从上边代码示例中也不难发现就是一个简化了的匿名内部类),那么怎么定义一个函数式接口呢?说来也简单,就是一个接口中有且只有一个抽象方法,如Runnable接口中只有一个run方法。
@FunctionalInterface
public interface Runnable {
public abstract void run();
}
其中@FunctionInterface并无实际作用,他只是标识了这个接口是一个函数式接口。那么为什么要强调是抽象方法呢?接口中不是只有抽象方法嘛?
interface Test{
static void t1(){
}
default void t2(){
}
}
可以看到其实在jdk1.8的时候接口中是允许存在静态方法以及default修饰的方法的。default方法存在原因很简单,在jdk1.8中出现了lambda表达式,自然java类库也是采用了这种方式的,那么截止到1.8之前java的类库已经是非常庞大了,如果将那些相关类库一一进行修改对java开发人员将会是极大的挑战,而且也会为用户造成极大的困扰,于是default方法应运而生。
如何定义一个函数式接口?
很简单,如前边提到的一样,一个接口中有且只有一个抽象方法,那么这个接口就是一个函数式接口。
@FunctionalInterface
interface TestFunction {
int apply(int a, int b);
}
该接口只有一个方法apply,参数一个两个int类型的a和b,返回值类型为int。那么怎么使用呢?
public class App {
int a;
int b;
public App(int a, int b) {
this.a = a;
this.b = b;
}
public static App build(int a, int b) {
return new App(a, b);
}
public int sum(TestFunction fun) {
return fun.apply(a, b);
}
public static void main(String[] args) {
System.out.println(App.build(100, 6).sum((x, y) -> x + y));
}
}
App类中有两个属性a和b,为了简化这里采用了静态工厂的方式创建对象也就是build方法。其中的sum方法就是lambda表达式的一种用法。入参是一个函数式接口,并且内部调用了函数式接口唯一的抽象方法。使用方法如main方法中所示
App.build(100, 6).sum((x, y) -> x + y)
调用sum方法之前的不难理解就是通过静态工厂创建了一个对象,而sum方法中的函数体就有点耐人寻味了。
其中x和y分别表示函数接口TestFunction.apply方法中的参数a和b。-> 在我看开来就是一个符号,没有什么实际意义,而且在不同的语言中使用的也是不同的,这个符号后边就是x+y,这个就是apply方法所要执行的内容,也可以将它看成是apply的方法体。
上诉代码中的调用还有一个简化写法,如下
App.build(100, 6).sum(Integer::sum)
其中Integer::sum表示的就是Integer的静态方法sum(),也就是Integer.sum(a,b)。这种写法称为方法引用。方法引用可以是静态方法引用也可以是普通方法引用,当然还可以是构造方法引用,也叫构造引用。
//普通方法引用
App app = App.build(10, 20);
app.sum(app::add);
class Test {
double result;
public Test(int result) {
this.result = result;
}
}
//构造引用
List<Test> tests = Stream.of(1, 2, 3).map(Test::new).toList();
构造引用采用了Stream的Api作为示例,Stream是jdk1.8提供的处理数据的流,整个API都是使用函数式编程,如果使用得当会简化开发。
lambda表达式的用法就记录到这里。下一节介绍jdk提供的函数式接口