文章目录
  1. 1. 1.概述Lambda表达式
    1. 1.1. 1.1 lambda表达式语法
    2. 1.2. 1.2 lambda表达式形式
    3. 1.3. 1.3 函数式接口
    4. 1.4. 1.4 方法引用
      1. 1.4.1. 1.4.1 方法引用格式:
      2. 1.4.2. 1.4.2 方法引用分类:
  2. 2. 2.从一个案例入手
    1. 2.1. 2.1 案例:直观体验Java8Stream操作

这是一个新的系列,主要讲Java8的lambda编程以及Stream流式编程相关的用法和案例。

这个系列脱胎于一个内部的分享,由于篇幅较长,内容较多,因此拆分成多篇文章进行发布,方便自己后续参考,也希望能够帮到读者朋友。

在这里重点感谢慕课网的 《告别996,开启Java高效编程之门》 课程。

本文是Java8函数式编程系列的第一篇,我们一起学习一下Java8函数式编程的基本概念及操作。

1.概述Lambda表达式

Lambda 表达式,也可称为闭包,它是推动 Java 8 发布的最重要新特性。

Lambda 允许把函数作为一个方法的参数(函数作为参数传递进方法中)。

使用 Lambda 表达式可以使代码变的更加简洁紧凑。

1.1 lambda表达式语法

(parameters) -> expression
或
(parameters) -> {statement};
  1. 我们可以将lambda表达式理解为一种代替原先匿名函数的新的编程方式

  2. 通过使用lambda表达式替换匿名函数的形式,将lambda表达式作为方法参数,实现判断逻辑参数化传递的目的。

1.2 lambda表达式形式

无参数

() -> System.out.println("code");

只有一个参数

name -> System.out.println("hello:" + name + "!");

没有参数,且逻辑复杂,需要通过大括号将多个语句括起来

() -> {    
    System.out.println("hello");    
    System.out.println("lambda");
}

包含两个参数的方法

BinaryOperator<Long> functionAdd = (x, y) -> x + y;
Long result = functionAdd.apply(1L, 2L);

包含两个参数且对参数显式声明

BinaryOperator<Long> functionAdd = (Long x, Long y) -> x + y;
Long result = functionAdd.apply(1L, 2L);

1.3 函数式接口

定义:

一个接口有且只有一个抽象方法;

函数式接口的实例可以通过 lambda 表达式、方法引用或者构造方法引用来创建;

注意

如果一个接口只有一个抽象方法,那么该接口就是一个函数式接口

如果我们在某个接口上声明了 @FunctionalInterface 注解,那么编译器就会按照函数式接口的定义来要求该接口

@FunctionInterface是Java8函数式接口注解,属于声明式注解,帮助编译器校验被标注的接口是否符合函数式接口定义

1.4 方法引用

我们通过Lambda表达式来实现匿名方法。

有些情况下,使用Lambda表达式仅仅是调用一些已经存在的方法;除了调用动作外,没有其他任何多余的动作,在这种情况下我们倾向于通过方法名来调用它,而Lambda表达式可以帮助我们实现这一要求。它使得Lambda在调用那些已经拥有方法名的方法的代码更简洁、更容易理解。

方法引用可以理解为Lambda表达式的另外一种表现形式。

方法引用是调用特定方法的lambda表达式的一种快捷写法,可以让你重复使用现有的方法定义,并像lambda表达式一样传递他们。

注意:

使用方法引用时,只写方法名,不写括号

1.4.1 方法引用格式:

格式: 目标引用    双冒号分隔符   方法名

eg:  String       ::        valueOf

1.4.2 方法引用分类:

1.指向静态方法的方法引用:当Lambda表达式的结构体是一个对象,并且调用其静态方法时,使用如下方式

表达式:
    (args) -> ClassName::staticMethod(args);
格式:    ClassName :: staticMethodName
eg:     Integer   :: valueOf

2.指向任意类型实例方法的方法引用:当直接调用对象的实例方法,则使用如下方式进行调用

表达式:
        (args) -> args.instanceMethod();
格式:  ClassName::instanceMethod;
eg:   String::indexOf  
            String::toString

3.指向现有对象的实例方法的方法引用:通过对象实例,方法引用实例方法

表达式:
    (args) -> object.instanceMethod(args);
    改写为
    (args) ->  object::instanceMethod;
eg:
    StringBuilder sb = new StringBuilder();
    Consumer<String> consumer = (String str) -> stringBuilder.append(str);
就可以改写为

Consumer<String> consumer = (String str) -> stringBuilder::append;

2.从一个案例入手

我们先看一个例子,宏观感受一下Java8 Lambda编程带来的便利(后续讲解Stream同样使用该案例)

2.1 案例:直观体验Java8Stream操作

Sku实体类: 标识一个电商下单商品信息对象

public class Sku {
    // 商品编号
    private Integer skuId;
    // 商品名称
    private String skuName;
    // 单价
    private Double skuPrice;
    // 购买个数
    private Integer totalNum;
    // 总价
    private Double totalPrice;
    // 商品类型
    private Enum skuCategory;

    public Sku() {
    }

    public Sku(Integer skuId, String skuName, Double skuPrice, Integer totalNum, Double totalPrice, Enum skuCategory) {
        this.skuId = skuId;
        this.skuName = skuName;
        this.skuPrice = skuPrice;
        this.totalNum = totalNum;
        this.totalPrice = totalPrice;
        this.skuCategory = skuCategory;
    }
        ...省略getter  setter...
}

SkuCategoryEnum枚举类: 商品类型枚举

public enum SkuCategoryEnum {
    CLOTHING(10, "服务类"),
    ELECTRONICS(20, "数码产品类"),
    SPORTS(30, "运动类"),
    BOOKS(40, "图书类")
    ;

    // 商品类型编号
    private Integer code;
    // 商品名称
    private String name;

    SkuCategoryEnum(Integer code, String name) {
        this.code = code;
        this.name = name;
    }
    ...省略getter...
}

CartService类: 初始化一批数据,模拟购物车

public class CartService {
    // 初始化购物车
    private static List<Sku> cartSkuList = new ArrayList<>();

    static {
        cartSkuList.add(new Sku(2, "无人机", 1000.00, 10, 1000.00, SkuCategoryEnum.ELECTRONICS));
        cartSkuList.add(new Sku(1, "VR一体机", 2100.00, 10, 2100.00, SkuCategoryEnum.ELECTRONICS));
        cartSkuList.add(new Sku(4, "牛仔裤", 60.00, 10, 60.00, SkuCategoryEnum.CLOTHING));
        cartSkuList.add(new Sku(13, "衬衫", 120.00, 10, 120.00, SkuCategoryEnum.CLOTHING));
        cartSkuList.add(new Sku(121, "Java编程思想", 100.00, 10, 100.00, SkuCategoryEnum.BOOKS));
        cartSkuList.add(new Sku(3, "程序化广告", 80.00, 10, 80.00, SkuCategoryEnum.BOOKS));
    }

    public static List<Sku> getCartSkuList() {
        return cartSkuList;
    }
}

我们直接看这个案例

private static List<Sku> cartSkuList = CartService.getCartSkuList();

@Test
public void show() {
    List<Integer> collect = cartSkuList.stream()
            // 方法引用
            .map(Sku::getSkuId)
            .distinct()
            .sorted()
            .collect(Collectors.toList());
    collect.stream().forEach(skuId -> {
        System.out.println(skuId.toString());
    });
}

简单解释下这段代码的意图:

首先获取购物车中商品列表,将该列表转换为流;收集商品列表中的所有商品编号(skuId),对商品编号进行去重,并进行自然排序(升序排列),最后收集为一个商品编号集合,并对该集合进行遍历打印。

我并没有加注释,但是相信聪明的你也一定能读懂上面这段代码,这正是Stream编程的特点:方法名见名知意,流式编程方式符合人类思考逻辑

运行该用例,打印如下:

1
2
3
4
13
121

打印结果符合我们的预期意图。

想象一下,如果不使用lambda+Stream方式,而是使用java7及之前的传统集合操作,实现上述操作我们的代码量有多少?保守估计至少是上述代码段的1.5倍。

这个案例可能还不具备说服力,接下来的文章中,我将通过一个对比案例来比较lambda编程与传统方式对集合操作的效率提升。

我们下文继续。



版权声明:

原创不易,洗文可耻。除非注明,本博文章均为原创,转载请以链接形式标明本文地址。

文章目录
  1. 1. 1.概述Lambda表达式
    1. 1.1. 1.1 lambda表达式语法
    2. 1.2. 1.2 lambda表达式形式
    3. 1.3. 1.3 函数式接口
    4. 1.4. 1.4 方法引用
      1. 1.4.1. 1.4.1 方法引用格式:
      2. 1.4.2. 1.4.2 方法引用分类:
  2. 2. 2.从一个案例入手
    1. 2.1. 2.1 案例:直观体验Java8Stream操作
Fork me on GitHub