一、Lambda 的正确使用姿势
正确使用 Lambda 表达式
Lambda 表达式是 Java 8 的一个重要的新特性,Lambda 表达式可以允许通过表达式来代替功能接口,Lambda 表达式也可以看做是一个匿名函数,也可以成为闭包。
Lambda 的一个重要作用之一就是简化匿名内部类的写法,但是 Lambda 表达式不能替换所有的匿名内部类的书写。
Lambda 表达式由三个部分组成,既 参数列表
、->
和 方法体
参数列表
:方法中的形参列表,函数式接口里的参数,如果没有参数可以将括号省略。->
:连接参数和方法的具体实现。方法体
:既代码块,具体的方法实现。
创建一个新的 Maven 项目 lambda-func-interfaces-traps,并添加 junit 依赖
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.12</version>
<scope>test</scope>
</dependency>
在 test 包下创建一个新的测试类 LambdaTest,用于演示无参和有参的 Lambda 表达式的写法。
无参数 Lambda 表达式
新建一个测试方法 testCreateThread,在创建一个 Runable 的线程需要在 new Thread()
的时候传入 Runable 接口的匿名实现,写法如下:
@Test
public void testCreateThread(){
new Thread(new Runnable() {
@Override
public void run() {
System.out.println("Thread Run !");
}
}).start();
}
执行上述测试代码,输出结果如下:
Thread Run !
此时编辑器提示匿名类部分代码可以使用 Lambda 表达式来替换
新增一个测试方法 testCreateThreadByLambda,使用 Lambda 表达式来代替匿名类。
@Test
public void testCreateThreadByLambda(){
new Thread(() -> {
System.out.println("Thread Run which create by Lambda!");
}).start();
}
执行上述代码,输出结果如下:
Thread Run which create by Lambda!
使用 Lambda 表达式替换匿名类的写法能够正常执行程序,输出 Thread run
。并且 Lambda 表达式的代码实现比较简单的话可以省略 {}
将实现代码写在一行内即可
有参数 Lambda 表达式
如果需要对集合对象或者数组对象进行排序,那么就需要实现 Comparator 接口,在接口的实现中定义比较的规则。
新增测试方法 testCompare,比较数组对象
@Test
public void testCompare(){
List<String> stringList = Arrays.asList("z","u","l","y");
Collections.sort(stringList, new Comparator<String>(){
@Override
public int compare(String o1, String o2) {
if (o1 == null){
return -1;
}
if (o2 == null){
return 1;
}
return o1.length() - o2.length();
}
});
}
同样在实现 Comparator 接口部分的代码,编辑器也提示可以使用 Lambda 来代替
创建新的测试方法 testCompareByLambdaWithParams
@Test
public void testCompareByLambdaWithParams(){
List<String> stringList = Arrays.asList("z","u","l","y");
Collections.sort(stringList, (o1, o2) -> {
if (o1 == null){
return -1;
}
if (o2 == null){
return 1;
}
return o1.length() - o2.length();
});
}
上述代码中 Lambda 表达式的参数并没有声明参数类型,这是因为编辑器可以根据上下文推断出变量的类型。
Lambda 表达式有哪些 “陷阱”
函数式接口的定义是一个接口有且只有一个抽象方法,但是函数式接口可以有多个非抽象方法
自定义函数式接口
首先在 com.traps.entity 包中定义一个 Hero 类
public class Hero {
public String team;
public String nickname;
public String skill;
public Hero(String team, String nickname, String skill) {
this.team = team;
this.nickname = nickname;
this.skill = skill;
}
@Override
public String toString() {
return "Hero{" +
"team='" + team + ''' +
", nickname='" + nickname + ''' +
", skill='" + skill + ''' +
'}';
}
}
接着定义一个接口 InjectCompoundV,并使用 函数式接口的注解 @FunctionInterface 标注
@FunctionalInterface
public interface InjectCompoundV {
// 通过注射五号化合物变成一个超级英雄
Hero transferToHomelander(String name);
}
在测试包下创建一个函数式接口的测试类 InjectCompoundVTest
public class InjectCompoundVTest {
public Map<String, Hero> stringHeroMap = new HashMap<>();
@Before
public void before(){
stringHeroMap.put("Butcher",new Hero("The Seven", "Homelander", "Laser eyes"));
}
@Test
public void test(){
InjectCompoundV injectCompoundV = (name) -> stringHeroMap.get(name);
System.out.println(injectCompoundV.transferToHomelander("Butcher"));
}
}
执行上测试代码,输出结果如下:
Hero{team='The Seven', nickname='Homelander', skill='Laser eyes'}
Buthcher 通过调用函数式接口 InjectCompoundV 实现 transferToHomelander 方法变成了另一个 Homelander。
修改测试方法,使用方法引用的方式实现接口的匿名类
@Test
public void test(){
InjectCompoundV injectCompoundV = stringHeroMap::get;
System.out.println(injectCompoundV.transferToHomelander("Butcher"));
}