函数式接口和Java8的Lambda表达式

Lambda

0x00 JS中的闭包以及Java中替代方法

在Java中,是不允许将一个方法作为参数传递给另一个函数,也不允许将一个方法作为值,返回给调用者,这就是所谓的不支持闭包。

但是JavaScript是可以这么玩的。比如:

1
2
3
4
5
6
7
getWebResource(function(result,error){
if(error){
doException();
}else{
analysisResult(result);
}
});

我们看到直接向getWebResource方法中传递了一个function,这个function用来解析网络请求结果。

但是Java不能这样,那Java一般是怎么玩耍的呢?



我们来看一段Android内的Java代码

1
2
3
4
5
6
7
loginBtn = (Button) findViewById(R.id.btn_login);
loginBtn.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View view) {
Login();
}
});

很明显,我们为loginBtn设置了一个点击监听回调函数,这函数中,传递了View.OnClickListener的匿名实例对象,我们通过实例对象,调用onClick(View view)方法。

这样,通过匿名对象,我们可以实现类似js中的闭包的功能,不过我们是通过匿名对象调用函数,而不是直接调用函数,还是有区别的

0x01 函数式接口

上面,我们看到View类中的OnClickListener接口。这个接口负责接收点击事件的回调

1
2
3
public interface OnClickListener {
void onClick(View var1);
}

这个接口有且仅有一个抽象方法,这种接口,我们称为函数式接口(FunctionalInterface)。这是Java8中添加的新的支持。
可以看看Java8的API
java.lang.FunctionalInterfaceAPI:https://docs.oracle.com/javase/8/docs/api/java/lang/FunctionalInterface.html
这是个标注函数式接口的注解。里面的注释,解释了函数式接口,和这个注解的用法。
就不翻译了,大致意思是:

  • 函数式接口就是:一个interface,里面只有一个抽象方法,其他什么都没有。
  • FunctionalInterface注解标注一个函数式接口,不能标注方法枚举属性这些。
  • 如果接口被标注了@FunctionalInterface,这个类就必须符合函数式接口的规范
  • 即使一个接口没有标注@FunctionalInterface,如果这个接口满足函数式接口规则,依旧被当作函数式接口。

注意:interface中重写Object类中的抽象方法,不会增加接口的方法数,因为接口的实现类都是Object的子类。

说完这些,我们该说一下神奇的Lambda表达式了

0x02 Lambda表达式

“Lambda 表达式”(Lambda expression)是一个匿名函数,Lambda表达式基于数学中的λ演算得名,直接对应于其中的Lambda抽象(Lambda abstraction),是一个匿名函数,即没有函数名的函数。Lambda表达式可以表示闭包(注意和数学传统意义上的不同)。
——百度百科

Java中的Lambda表达式有三种形式,我们来举3个例子

1
2
3
4
5
6
7
8
9
10
11
//1.
() -> System.out.println("Hello Lambda");
//2.
(number1, number2) -> int a = number1 + number2;
//3.
(number1, number2) -> {
int a = number1 + number2;
System.out.println(a);
}

大致形式就是

1
(param1, param2, param3, param4…)->{ doing……};

就是一个匿名的函数式接口的缩写。

很明显,在之前的代码中,我们用到了匿名对象,这个匿名对象就是一个函数式接口的子对象,所以,在Java8中可以使用Lambda表达式
我们把刚刚的代码简化成Lambda表达

1
2
loginBtn = (Button) findViewById(R.id.btn_login);
loginBtn.setOnClickListener((view)-> Login());

瞬间简洁了,有木有?

然后呢?因为有了函数式接口的标准,Java8中的List,有了一个新方法,叫forEach,下面演示一下这个方法。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
package com.zing.lambda_demo;
import java.util.Arrays;
import java.util.List;
/**
* Created by zing on 2016/12/21.
*/
public class FunctionalInterfaceTest {
public static void main(String[] args) {
List<Integer> demoList = Arrays.asList(1, 2, 3, 4, 5);
demoList.forEach((num) -> System.out.println(num));
}
}

运行结果

当然,Lambda表达式虽然简洁,也是有缺点的:就是降低了代码的可读性,比如一眼看不出来原来的函数式接口是什么。
所以什么情况用Lambda,要多写多练才会用的恰当。

ps:我就不解释forEach具体方法了,贴俩图你看看实现和注解

forEach方法实现

Consumer函数式接口

谢谢收看,我的博客:http://www.azing.xyz 欢迎光顾。


love&peace
FS全栈计划目录:https://micorochio.github.io/fs-plan/