你有没有掉进去过这些 Lambda 的 “陷阱“

一、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 表达式来替换

image.png

新增一个测试方法 testCreateThreadByLambda,使用 Lambda 表达式来代替匿名类。

扫描二维码关注公众号,回复: 14330561 查看本文章
@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"));
}

猜你喜欢

转载自juejin.im/post/7113949902102593550