Java 并发编程——Lambda 表达式

简介

Java 8 是 Java 语言发展史上的一个重要里程碑,更新了许多新特性,包括 Lambda函数式编程等等。

例:

List<Fruit> fruits = Arrays.asList(
    new Fruit("香蕉"),
    new Fruit("苹果"),
    new Fruit("梨子"),
    new Fruit("西瓜"),
    new Fruit("荔枝")
);

进行遍历,打印水果的名字:

for (int i = 0; i < fruits.size(); i++) {
  Fruit f = fruits.get(i);
  System.out.println(f.getName());
}

如果使用新特性:

fruits.forEach(f -> {
  System.out.println(f.getName());
});

无类型参数

基本定义

Lambda 表达式的基本结构是:

f -> { }

作为参数的变量名,是可以自定义的,不是固定的

Lambda 表达式在功能上等同于一个匿名方法:

public void unknown(f) {
  System.out.println(f.getName());
}

类别识别

f 变量的类型是什么呢?

答案:类型是系统根据上下文 自动 识别的。

看简介的代码:

List<Fruit> fruits = Arrays.asList(......);

fruits.forEach(f -> {
  System.out.println(f.getName());
});

forEach() 方法表示循环遍历 fruits 集合,每次循环时 f 变量指代当前遍历到的元素。

由于 fruits 变量的类型使用泛型语法定义为 List<Fruit>,表示集合中元素的类型是 Fruit

所以,f 变量的类型自然是 Fruit,就可以调用 Fruit 类的方法了。

forEach() 方法是 Java 集合的循环遍历的方法。

Lambda 表达式要配合上下文、跟其他方法配合使用,而不是一个独立的语句。

错误例子:

public static void main(String[] args) {
  args[0] -> {
    System.out.println(args[0]);
  }
}

案例:

下面的代码演示Collections中sort()方法的排序功能。可以使用 Lambda 表达式进行代码优化:

List<Student> students = new ArrayList<Student>();
students.add(new Student(111, "bbbb", "london"));
students.add(new Student(131, "aaaa", "nyc"));
students.add(new Student(121, "cccc", "jaipur"));

// 实现升序排序
Collections.sort(students, (student1, student2) -> {
  // 第一个参数的学号 vs 第二个参数的学号
  return student1.getRollNo() - student2.getRollNo();
});

students.forEach(s -> System.out.println(s));

Lambda 表达式又有了一些新的写法:

多参数

箭头(->)前表示参数变量,有 多个参数 的时候,必须使用小括号包裹:()

(student1, student2) -> {}

无参数

箭头(->)前表示参数变量,没有参数 的时候,必须使用小括号:()

() -> {}

单条执行语句

箭头(->)后的执行语句 只有一条 时,可以不加大括号 {} 包裹

s -> System.out.println(s);

都 推荐 加上大括号 {},边界明确。

有类型参数

大多数情况下,使用 Lambda 表达式的目的是为了代码尽量简洁,所以尽量省略了参数变量的类型。

如果代码比较复杂,而为了易阅读、易维护,也可以为参数变量指定类型。

注意:

即使只有一个参数,也必须使用小括号 () 包裹,否则会出错。

fruits.forEach((Fruit f) -> {
  System.out.println(f.getName());
});

引用外部变量

 

Lambda 表达式 {} 内的执行语句,除了能引用参数变量以外,还可以引用外部的变量。

List<Fruit> fruits = Arrays.asList(......);
String message = "水果名称:";

fruits.forEach(f -> {
  System.out.println(message + f.getName());
});

message = "";

可以看到报错:

  错误: 从lambda 表达式引用的本地变量必须是最终变量或实际上的最终变量

规范一:

因为使用 Lambda 表达式有规范:引用的 局部变量 不允许被修改。(即使写在表达式后面也不行)

下列代码修改了局部变量也是错误的:

String message;

fruits.forEach(f -> {
  message = "水果名称:";
});

 Lambda 表达式引用的 局部变量 即使不声明为 final,也要具备 final 的特性:变量值初始化后不允许被修改。

表达式外实际值赋值一次的写法是允许的:

String message;
message = "水果名称:";

 规范二:

使用 Lambda 表达式还有一点需要注意:参数不能与 局部变量 同名。下列写法是错误的:

String f = "水果名称:";
fruits.forEach(f -> {});

双冒号(::)操作符

Java 8 支持了一种新的语法:双冒号 “::”,这也是一种 Lambda 写法 ,是与 Lambda 表达式相关的一个重要特性。

前面例子中,只有一条执行语句的 Lambda 表达式:

List<String> names = Arrays.asList("zhangSan", "LiSi", "WangWu");

names.forEach(n -> {
  System.out.println(n);
});

可以进一步简化为:

names.forEach(System.out::println);

forEach() 的作用是遍历集合。

这段代码的重点是使用 :: 时,系统每次遍历取得的元素(n),会 自动 作为参数传递给 System.out.println() 方法打印输出:

System.out::println 等同于 n -> {System.out.println(n);}

:: 语法干脆省略了参数变量,所以读这段代码需要相互对照、并且自行脑补

语法含义

双冒号 :: 语法,当然也是不能独立使用的,需要配合特定的方法 

不同用法

用法一:静态方法调用

使用 LambdaTest::print 代替 f -> LambdaTest.print(f),简化了代码。

用法二:调用非静态方法

print() 方法不再标识为 static,于是需要实例对象来调用。

fruits.forEach(new LambdaTest()::print);

只是简写了:

LambdaTest lt = new LambdaTest();
fruits.forEach(lt::print);

 System.out::println 语句就是调用非静态方法 println(),因为 System.out 指代的是一个实例对象。

用法三:多参数

Collections.sort(students, (student1, student2) -> {
  // 第一个参数的学号 vs 第二个参数的学号
  return student1.getRollNo() - student2.getRollNo();
});

就碰到了多参数的情况。如果把比较的过程定义成一个方法:

private static int compute(Student s1, Student s2) {
  ... ...
  ... ...
}

 那么,排序过程就可以简写为:

Collections.sort(students, SortTest::compute);

注意,系统会 自动 获取上下文的参数,并按上下文定义的 顺序 传递给指定的方法。所谓 顺序 就是 Lambda 表达式 () 中的顺序 。

与变量名无关,s1 s2 命名只是便于理解

用法四:父类方法

super 关键字的作用是在子类中引用父类的属性或者方法。那么同样,:: 语法也可以用 super 关键字调用父类的非静态方法。

import java.util.Arrays;
import java.util.List;

public class LambdaTest extends LambdaExample {
  public static void main(String[] args) {
    List<Fruit> fruits = Arrays.asList(
            new Fruit("香蕉"),
            new Fruit("苹果"),
            new Fruit("梨子"),
            new Fruit("西瓜"),
            new Fruit("荔枝")
    );

    LambdaTest at = new LambdaTest();
    at.print(fruits);
  }

  public void print(List<Fruit> fruits){
    fruits.forEach(super::print);
    }
}

class LambdaExample {
    public void print(Fruit f){
        System.out.println(f.getName());
    }
}

  • 15
    点赞
  • 7
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

DF_Orange

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值