Lambda表达式

Lambda表达式是Java8的新特性使得代码更为简洁易懂。

如当我们需要依次打印某集合的内容(如水果名字集合),之前我们使用 for 循环来完成打印,但使用Lambda表达式则可以:

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

其中 f -> {},就是 Lambda表达式 

其中 f 表示参数变量,其类型取决于上下文的需求,即Lambda表达式需要配合上下文,配合其他语法使用,而不能成为单独的语句。

而 {} 中写入的是方法的主体。

多参数

箭头(->)前表示参数变量,当存在多个参数时,需要用小括号()包裹:

(student1, student2) -> {}

无参数

同理,当没有参数变量时,也需要用小括号()包裹:

() -> {}

注:当箭头后面只有一条执行语句时,可以不用大括号包裹

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

但在实际使用中最好无论多少条语句都加上 {} 。

当然,所写的参数也可以带类型如:

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引用外部变量存在规范:

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

当然写在 { } 内的修改也是不行的。即Lamdba表达式引用的局部变量具备 final 的特性。

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

  • 规范二:参数不能与局部变量同名,如下错误
String f = "水果名称:";
fruits.forEach(f -> {});

双冒号(::)操作符

双冒号可以简化操作:

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

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

简化为:

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

使用双冒号时,系统每次遍历取得的元素(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);

效果是一样的。

  • 用法三:多参数
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 表达式 () 中的顺序。

  • 用法四:父类方法

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