java 8 新特性 - lambda表达式

lambda表达式是java 8 发布的最重要新特性之一,这是这些年来Java语言最让人激动的一个变化。lambda表达式是一个可传递的代码块,也可称为闭包,使用它可以让代码变的更加简洁紧凑。

lambda表达式的语法

lambda表达式的语法格式如下:

1
2
3
(parameters) -> expression

(parameters) -> { statements; }

其具体特征如下:

  • 可选类型声明: 不需要声明参数类型,编译器可以统一识别参数值。
  • 可选的参数圆括号: 一个参数无需定义圆括号,但多个参数需要定义圆括号。
  • 可选的大括号: 如果主体包含了一个语句,就不需要使用大括号。
  • 可选的返回关键字: 如果主体只有一个表达式返回值则编译器会自动返回值,大括号需要指定明表达式返回了一个数值。

实例

1
2
3
4
5
6
7
8
9
10
11
12
13
14
// 1. 不需要参数,返回值为 5  
() -> 5

// 2. 接收一个参数(数字类型),返回其2倍的值
x -> 2 * x

// 3. 接受2个参数(数字),并返回他们的差值
(x, y) -> x – y

// 4. 接收2个int型整数,返回他们的和
(int x, int y) -> x + y

// 5. 接受一个 string 对象,并在控制台打印,不返回任何值(看起来像是返回void)
(String s) -> System.out.print(s)

注释: 如果一个lambda表达式只在某些分支返回一个值,而在另外一些分支不返回值,这是不合法的。例如,(int x) -> { if (x > 0) return 1; }就不合法。

函数式接口

函数式接口(Functional Interface)就是一个有且仅有一个抽象方法,但是可以有多个非抽象方法的接口。函数式接口可以很好的支持lambda。

例如:Array.sort的第二个参数需要一个Comparator实例,Comparator就是只有一个方法的接口,所以可以提供一个lambda表达式:

1
Arrays.sort(lists, (first, second) -> first.length() - second.length());

方法引用

先看示例:

1
2
3
Timer t = new Timer(1000, event -> System.out.println(event));
// 也可以这样写
Timer t = new Timer(1000, System.out::println);

表达式System.out::println是一个方法引用,它等价于lambda表达式 x -> System.out.println(x)

从例子可以看出,要用::操作符分隔方法名与对象或类名。主要有3种情况:

  • object::instanceMethod
  • Class::staticMethod
  • Class::instanceMethod

例如:

1
2
3
4
5
6
7
8
// 第一种情况
System.out::println => x -> System.out.println(x)

// 第二种情况
Math::pow => (x, y) -> Math.pow(x, y)

// 第三种情况
String::compareToIgnoreCase => (x, y) -> x.compareTolgnoreCase(y)

构造器引用

构造器引用与方法引用很类似,只不过方法名为new

1
2
ArrayList<String> names = ...;
Stream<Person> stream = names.stream().map(Person::new);

Person::new是Person构造器的一个引用,具体是哪一个构造器取决于上下文。

变量作用域

lambda表达式有3个部分:

  1. 一个代码块;
  2. 参数;
  3. 自由变量的值,这是指非参数而且不在代码中定义的变量。

在lambda表达式中,只能引用值不会改变的变量。之所以有这个限制是因为如果在lambda表达式中改变变量,并发执行多个动作时就会不安全。例如:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
public static void countDown(int start, int delay) {
ActionListener listener = event ->
{
start--; // Error: Can't mutate captured variable
System.out.println(start);
}
new Timer(delay, listener).start();
}

public static void repeat(String text, int count) {
for (int i =1; i <= count; i++) {
ActionListener listener = event -> {
System.out.println(i + ": " + text);
// Error: Cannot refer to changing i
}
new Timer(1000, listener).start();
}
}

在一个lambda表达式中使用this关键字时,是指创建这个lambda表达式的方法的this参数。

z.h.l wechat
欢迎您扫一扫,订阅我的微信公众号