Java Lambda从入门到精通一 初探

从Java8开始,引入了一个新功能,那就是Lambda。

为什么加入了这个功能呢?

因为现在脚本语言或者说函数式编程越来越流行,函数式编程的优势在于代码更简洁了,而且更易于理解,更重要的式代码更加灵活、功能也变的更强大,还有一个优点就是函数式编程,对程序员写代码更加高效了,因为以前你实现一个功能可能要写十几行甚至几十行代码,现在使用了Java Lambda的函数式编程方式,可能几行代码,甚至一行代码就能搞定,即便应对复杂的编写逻辑,也能灵活应对,简化编写过程。

简单来说,Java的Lambda表达式一是简化代码,让编写更流程;二是将函数变为了一等公民,能像变量一样传递;三是对并行的支持。

说完了有点,我们用代码来体验一下Lambda的魅力。

比如我有一筐苹果,我要按照颜色分类,分为红苹果和绿苹果。我们先定义一个苹果类,如下:

package com.itzhimei.study.lambda.unit2;

import lombok.Data;

/**
 * @Auther: www.itzhimei.com
 * @Description:
 */
@Data
public class Apple {

    private String color;

    private int weight;

    public Apple(String color, int weight) {
        this.color = color;
        this.weight = weight;
    }
}

我现在要根据颜色把苹果分成红苹果和绿苹果,你可能会想到,用if判断一下苹果颜色就可以了,根据不同颜色,放到不同分组重,代码如下:

        for(Apple a: apples) {
            if(a.getColor().equals("红苹果")) {
                System.out.println("发现一个红苹果");
            } else {
                System.out.println("发现一个绿苹果");
            }
        }

这时我们Java8之前的常用写法。但是如果现在需求变复杂了,我不仅要区分颜色,还要区分重量,甚至是颜色和重量一起分,红色苹果大于150g的找出来,该怎么做?

还用 if(条件A && 条件B && 条件C &&…) ,或者if-else嵌套吗?

是可以的,但是代码不够优雅,也不好维护,因为我们实际的业务可能比这复杂很多,这里的if就变成了一个庞大的逻辑判断代码块,稍有不慎就改出bug了。

我们再来看看建模改进版。

有的同学会说,我们可以根据不同的逻辑,创建一个处理类,根据不同需求,使用不同除了处理类(这就是策略模式),这样确实可以,而且代码的扩展性也变好了,如果新增了其他逻辑需求,我新增一个对应的处理类就可以了。

代码如下:

1、新建一个抽象处理类

package com.itzhimei.study.lambda.unit2;

/**
 * @Auther: www.itzhimei.com
 * @Description:
 */
public interface AppleFormatter {

    String accept(Apple apple);
}

2、新建判断苹果重量的实现类

package com.itzhimei.study.lambda.unit2;

/**
 * @Auther: www.itzhimei.com
 * @Description:
 */
public class AppleFormatterA implements AppleFormatter {
    @Override
    public String accept(Apple apple) {
        return apple.getWeight()>150?"重苹果":"轻苹果";
    }
}

3、新建用于打印苹果重量的处理类

package com.itzhimei.study.lambda.unit2;

/**
 * @Auther: www.itzhimei.com
 * @Description:
 */
public class AppleFormatterB implements AppleFormatter {
    @Override
    public String accept(Apple apple) {
        return apple.getColor() + "的重量是:" + apple.getWeight() + "g";
    }
}

4、测试

package com.itzhimei.study.lambda.unit2;

import java.util.ArrayList;
import java.util.List;

/**
 * @Auther: www.itzhimei.com
 * @Description: 行为参数化  建模:每种逻辑实现接口里的方法,策略模式,优点易于理解,符合我们开发逻辑; 缺点:类多
 */
public class Lambda2_2_1 {

    public static void main(String[] args) {
        List<Apple> apples = new ArrayList<>();
        Apple a1 = new Apple("红苹果",155);
        Apple a2 = new Apple("绿苹果",136);
        Apple a3 = new Apple("红苹果",169);
        apples.add(a1);
        apples.add(a2);
        apples.add(a3);

        printApple(apples, new AppleFormatterA());
        printApple(apples, new AppleFormatterB());

    }

    public static void printApple(List<Apple> apples, AppleFormatter af) {
        for(Apple a : apples) {
            System.out.println(af.accept(a));
        }
    }
}

我们在测试代码中定义了一个使用策略类的方法printApple,根据传入的策略类的不同,就能产生不同的功能效果。

输出:

重苹果
轻苹果
重苹果
红苹果的重量是:155g
绿苹果的重量是:136g
红苹果的重量是:169g

这种策略模式的改进,已经一定程度上能应对需求的变化了,但是,缺点是类会非常多。

为了简化类的数量,java8之前,我们可以使用匿名类进行改造,也能达到上面的效果。

package com.itzhimei.study.lambda.unit2;

import java.util.ArrayList;
import java.util.List;

/**
 * @Auther: www.itzhimei.com
 * @Description: 行为参数化  使用内部类简化代码,减少了类的创建,但是代码不易读
 */
public class Lambda2_2_2 {

    public static void main(String[] args) {
        List<Apple> apples = new ArrayList<>();
        Apple a1 = new Apple("红苹果",155);
        Apple a2 = new Apple("绿苹果",136);
        Apple a3 = new Apple("红苹果",169);
        apples.add(a1);
        apples.add(a2);
        apples.add(a3);

        printApple(apples, new AppleFormatter(){
            @Override
            public String accept(Apple apple) {
                return apple.getWeight()>150?"重苹果":"轻苹果";
            }
        });
        printApple(apples, new AppleFormatter(){
            @Override
            public String accept(Apple apple) {
                return apple.getColor() + "的重量是:" + apple.getWeight() + "g";
            }
        });

    }

    public static void printApple(List<Apple> apples, AppleFormatter af) {
        for(Apple a : apples) {
            System.out.println(af.accept(a));
        }
    }
}

但是匿名类的缺点就是不易读,难理解,而且如果大量使用匿名类,代码就非常不美观了。

那么接下来就是我们今天的主角了,看看使用lambda后代码的效果。

package com.itzhimei.study.lambda.unit2;

import java.util.ArrayList;
import java.util.List;

/**
 * @Auther: www.itzhimei.com
 * @Description: 行为参数化  使用lambda表达式简化代码
 */
public class Lambda2_2_3 {

    public static void main(String[] args) {
        List<Apple> apples = new ArrayList<>();
        Apple a1 = new Apple("红",155);
        Apple a2 = new Apple("绿",136);
        Apple a3 = new Apple("红",169);
        apples.add(a1);
        apples.add(a2);
        apples.add(a3);

        printApple(apples, (Apple apple) -> apple.getColor().equals("红")?"红苹果":"绿苹果");
        System.out.println("---------------------------");
        printApple(apples, (Apple apple) -> apple.getWeight()>150?"重苹果":"轻苹果");

    }

    public static void printApple(List<Apple> apples, AppleFormatter af) {
        for(Apple a : apples) {
            System.out.println(af.accept(a));
        }
    }
}

重点看这里:

printApple(apples, (Apple apple) -> apple.getColor().equals("红")?"红苹果":"绿苹果");

这就是lambda的最简单的用法之一。我们将原来写在策略类中的判断或者写在匿名类中的判断逻辑,变成了一个表达式,却实现了同样的效果,也仅仅只是一行代码。

上面的代码看不懂不要紧,重点是体会一下lambda的效果,后面我会一点一点学会,学懂,直到熟练应用。

上面这种写法,就叫做:行为参数化。

也就是你将一个类似方法的东西,作为参数,传入另了一个方法,在这个传入的方法中,再应用这个被当作参数的方法,这就是行为参数化。

仅仅是这一个特性,就能让我们的代码变的无限强大和灵活,这个我们通过后面的学习再来体会。

本文及后续课程参考内容:《Java 8实战》、《精通lambda表达式:Java多核编程》