Lambda 表达式,也可称为闭包,它是推动 Java 8 发布的最重要新特性。
Lambda 允许把函数作为一个方法的参数(函数作为参数传递进方法中)。
使用 Lambda 表达式可以使代码变的更加简洁紧凑。
参考资料
语法
lambda
表达式的语法格式如下:
(parameters) -> expression
或
(parameters) ->{ statements; }
以下是lambda表达式的重要特征:
- 可选类型声明:不需要声明参数类型,编译器可以统一识别参数值。
- 可选的参数圆括号:一个参数无需定义圆括号,但多个参数需要定义圆括号。
- 可选的大括号:如果主体包含了一个语句,就不需要使用大括号。
- 可选的返回关键字:如果主体只有一个表达式返回值则编译器会自动返回值,大括号需要指定明表达式返回了一个数值。
Lambda 表达式实例
Lambda 表达式的简单例子:
1 | // 1. 不需要参数,返回值为 5 |
基本例子
for 循环
假设有一个玩家List
,程序员可以使用 for
语句 (“for 循环
“)来遍历,在Java SE 8
中可以转换为另一种形式:
1 | String[] atp = {"Rafael Nadal", "Novak Djokovic", |
Runnable
正如您看到的,lambda表达式
可以将我们的代码缩减到一行。 另一个例子是在图形用户界面程序中,匿名类可以使用lambda表达式
来代替。 同样,在实现Runnable接口
时也可以这样使用:
1 | // 使用匿名内部类 |
下面是使用lambdas
来实现 Runnable
接口 的示例:
1 | // 1.1使用匿名内部类 |
使用Lambdas排序集合
在 Java
中,Comparator 类
被用来排序集合。 在下面的例子中,我们将根据球员的 name
, surname
, name
长度 以及最后一个字母。 和前面的示例一样,先使用匿名内部类来排序,然后再使用lambda表达式
精简我们的代码。
在第一个例子中,我们将根据name
来排序list
。 使用旧的方式,代码如下所示:
1 | String[] players = {"Rafael Nadal", "Novak Djokovic", |
使用lambdas
,可以通过下面的代码实现同样的功能:
1 | // 1.2 使用 lambda expression 排序 players |
其他的排序如下所示。 和上面的示例一样,代码分别通过匿名内部类和一些lambda表达式
来实现Comparator
:
1 | // 1.1 使用匿名内部类根据 surname 排序 players |
自写例子
在 Java8Tester.java 文件输入以下代码:
1 | public class Java8Tester { |
执行以上脚本,输出结果为:
$ javac Java8Tester.java
$ java Java8Tester
1 | 10 + 5 = 15 |
使用 Lambda
表达式需要注意以下两点:
Lambda
表达式主要用来定义行内执行的方法类型接口,例如,一个简单方法接口。在上面例子中,我们使用各种类型的Lambda表达式
来定义MathOperation
接口的方法。然后我们定义了sayMessage
的执行。Lambda
表达式免去了使用匿名方法的麻烦,并且给予Java
简单但是强大的函数化的编程能力。
ps: 更要注意的是, lambda
是对函数式接口的隐式调用。
变量作用域
lambda
表达式只能引用标记了 final
的外层局部变量,这就是说不能在 lambda
内部修改定义在域外的局部变量,否则会编译错误。
在 Java8Tester.jav
文件输入以下代码:
1 | public class Java8Tester { |
执行以上脚本,输出结果为:
$ javac Java8Tester.java
$ java Java8Tester
Hello! Runoob
我们也可以直接在 lambda
表达式中访问外层的局部变量:
Java8Tester.java
文件
1 | public class Java8Tester { |
lambda 表达式
的局部变量可以不用声明为 final
,但是必须不可被后面的代码修改(即隐性的具有 final
的语义)
1 | int num = 1; |
在 Lambda 表达式
当中不允许声明一个与局部变量同名的参数或者局部变量。
1 | String first = ""; |
Lambdas和Streams
Stream
是对集合的包装,通常和lambda
一起使用。 使用lambdas
可以支持许多操作,如 map
, filter
, limit
, sorted
, count
, min
, max
, sum
, collect
等等。
同样,Stream
使用懒运算,他们并不会真正地读取所有数据,遇到像getFirst()
这样的方法就会结束链式语法。 在接下来的例子中,我们将探索lambdas
和streams
能做什么。
我们创建了一个Person
类并使用这个类来添加一些数据到list
中,将用于进一步流操作。 Person
只是一个简单的POJO
类:
1 | public class Person { |
接下来,我们将创建两个list
,都用来存放Person
对象:
1 | List<Person> javaProgrammers = new ArrayList<Person>() { |
现在我们使用forEach
方法来迭代输出上述列表:
1 | System.out.println("所有程序员的姓名:"); |
我们同样使用forEach
方法,增加程序员的工资5%
:
1 | System.out.println("给程序员加薪 5% :"); |
我们也可以定义过滤器,然后重用它们来执行其他操作:
1 | // 定义 filters |
使用limit
方法,可以限制结果集的个数:
1 | System.out.println("最前面的3个 Java programmers:"); |
排序呢? 我们在stream
中能处理吗? 答案是肯定的。 在下面的例子中,我们将根据名字和薪水排序Java
程序员,放到一个list
中,然后显示列表:
1 | System.out.println("根据 name 排序,并显示前5个 Java programmers:"); |
如果我们只对最低和最高的薪水感兴趣,比排序后选择第一个/最后一个 更快的是min
和max
方法:
1 | System.out.println("工资最低的 Java programmer:"); |
上面的例子中我们已经看到 collect
方法是如何工作的。 结合 map
方法,我们可以使用 collect
方法来将我们的结果集放到一个字符串,一个 Set
或一个 TreeSet
中:
1 | System.out.println("将 PHP programmers 的 first name 拼接成字符串:"); |
Streams
还可以是并行的(parallel
)。 示例如下:
1 | System.out.println("计算付给 Java programmers 的所有money:"); |
我们可以使用summaryStatistics
方法获得stream
中元素的各种汇总数据。 接下来,我们可以访问这些方法,比如getMax
, getMin
, getSum
或getAverage
:
1 | //计算 count, min, max, sum, and average for numbers |