函数式接口
函数式接口(functional interface 也叫功能性接口)。简单来说,函数式接口是只包含一个方法的接口。比如Java标准库中的java.lang.Runnable和 java.util.Comparator都是典型的函数式接口。
java 8提供@FunctionalInterface作为注解,这个注解是非必须的,只要接口符合函数式接口的标准(即只包含一个方法的接口),虚拟机会自动判断,但最好在接口上使用注解@FunctionalInterface进行声明,以免团队的其他人员错误地往接口中添加新的方法。
Java中的lambda无法单独出现,它需要一个函数式接口来盛放,lambda表达式方法体其实就是函数接口的实现。
下面的接口就是一个函数式接口
@FunctionalInterface
public interface Runnable {
public abstract void run();
}
lambda语法
包含三部分:1、一个括号内用逗号分隔的形式参数,参数是函数式接口里面方法的参数2、一个箭头符号:->3、方法体,可以是表达式和代码块。
(parameters) -> expression 或者 (parameters) -> { statements; }
例1、用lambda表达式实现Runnable
我开始使用Java 8时,首先做的就是使用lambda表达式替换匿名类,而实现Runnable接口是匿名类的最好示例。看一下Java 8之前的runnable实现方法,需要4行代码,而使用lambda表达式只需要一行代码。我们在这里做了什么呢?那就是用() -> {}代码块替代了整个匿名类。
1 // Java 8之前:
2 new Thread(new Runnable() {
3 @Override
4 public void run() {
5 System.out.println("Before Java8, too much code for too little to do");
6 }
7 }).start();
1 //Java 8方式:
2 new Thread( ()->System.out.println("In Java8, Lambda expression rocks !!") ).start();
输出:
1 too much code, for too little to do
2 Lambda expression rocks !!
这个例子向我们展示了Java 8 lambda表达式的语法。你可以使用lambda写出如下代码:
1 (params) -> expression
2 (params) -> statement
3 (params) -> { statements; }
例如,如果你的方法不对参数进行修改、重写,只是在控制台打印点东西的话,那么可以这样写:
1 () -> System.out.println("Hello Lambda Expressions");
如果你的方法接收两个参数,那么可以写成如下这样:
(int even, int odd) -> even + odd
顺便提一句,通常都会把lambda表达式内部变量的名字起得短一些。这样能使代码更简短,放在同一行。所以,在上述代码中,变量名选用a、b或者x、y会比even、odd要好。
例2、使用Java 8 lambda表达式进行事件处理
如果你用过Swing API编程,你就会记得怎样写事件监听代码。这又是一个旧版本简单匿名类的经典用例,但现在可以不这样了。你可以用lambda表达式写出更好的事件监听代码,如下所示:
1 // Java 8之前:
2 JButton show = new JButton("Show");
3 show.addActionListener(new ActionListener() {
4 @Override
5 public void actionPerformed(ActionEvent e) {
6 System.out.println("Event handling without lambda expression is boring");
7 }
8 });
1 // Java 8方式:
2 show.addActionListener((e) -> {
3 System.out.println("Light, Camera, Action !! Lambda expressions Rocks");
4 });
Java开发者经常使用匿名类的另一个地方是为 Collections.sort() 定制 Comparator。在Java 8中,你可以用更可读的lambda表达式换掉丑陋的匿名类。我把这个留做练习,应该不难,可以按照我在使用lambda表达式实现 Runnable 和 ActionListener 的过程中的套路来做。
例3、使用lambda表达式对列表进行迭代
如果你使过几年Java,你就知道针对集合类,最常见的操作就是进行迭代,并将业务逻辑应用于各个元素,例如处理订单、交易和事件的列表。由于Java是命令式语言,Java 8之前的所有循环代码都是顺序的,即可以对其元素进行并行化处理。如果你想做并行过滤,就需要自己写代码,这并不是那么容易。通过引入lambda表达式和默认方法,将做什么和怎么做的问题分开了,这意味着Java集合现在知道怎样做迭代,并可以在API层面对集合元素进行并行处理。下面的例子里,我将介绍如何在使用lambda或不使用lambda表达式的情况下迭代列表。你可以看到列表现在有了一个 forEach() 方法,它可以迭代所有对象,并将你的lambda代码应用在其中。
1 // Java 8之前:
2 List features = Arrays.asList("Lambdas", "Default Method", "Stream API", "Date and Time API");
3 for (String feature : features) {
4 System.out.println(feature);
5 }
1 // Java 8之后:
2 List features = Arrays.asList("Lambdas", "Default Method", "Stream API", "Date and Time API");
3 features.forEach(feature -> System.out.println(feature));
4
5 // 使用Java 8的方法引用更方便,方法引用由::双冒号操作符标示,
6 // 看起来像C++的作用域解析运算符
7 features.forEach(System.out::println);
输出:
1 Lambdas
2 Default Method
3 Stream API
4 Date and Time API
列表循环的最后一个例子展示了如何在Java 8中使用方法引用(method reference)。你可以看到C++里面的双冒号、范围解析操作符现在在Java 8中用来表示方法引用。