Lambda表达式

Java lambda表达式是Java 8中新添加的.Java lambda表达式是Java进入函数式编程的第一步。 lambda表达式可以创建不属于一个类的函数。lambda表达式可以像对象一样被传递,并按需执行。

Lambda表达式和单一方法接口

函数式编程通常用于实现事件监听器。Java中的事件监听器通常定义为含有单一方法的Java接口。下面是一个虚拟的单一方法接口的例子:

public interface StateChangeListener {

    public void onStateChange(State oldState, State newState);

}

这个Java接口定义了一个单一的方法,只要状态改变(在被检测到的情况下)就被调用。

在Java 7中,你必须实现这个接口来监听状态变化。想象一下,你有一个名为StateOwner的类,它可以注册状态事件监听器。下面是一个例子:

public class StateOwner {

    public void addStateListener(StateChangeListener listener) { ... }

}

在Java 7中,你可以使用匿名接口实现来添加事件侦听器,如下所示:

StateOwner stateOwner = new StateOwner();

stateOwner.addStateListener(new StateChangeListener() {

    public void onStateChange(State oldState, State newState) {
        // 实现代码
    }
});

首先创建一个StateOwner实例。然后,将StateChangeListener接口的匿名实现作为侦听器添加到StateOwner实例中。

在Java 8中,你可以使用Java lambda表达式添加事件侦听器,如下所示:

StateOwner stateOwner = new StateOwner();

stateOwner.addStateListener(
    (oldState, newState) -> System.out.println("State changed")
);

lambda表达式是这部分:

(oldState, newState) -> System.out.println("State changed")

lambda表达式与addStateListener()方法参数的参数类型相匹配。如果lambda表达式匹配参数类型(在本例中是StateChangeListener接口),那么lambda表达式就变成一个实现与该参数相同接口的函数。

Java lambda表达式只能用于与其匹配的类型是单个方法接口的情况。在上面的例子中,lambda表达式被用作参数,其中参数类型是StateChangeListener接口。这个接口只有一个方法。因此,lambda表达式与该接口成功匹配。

Lambda表达式与接口相匹配

单个方法接口有时也被称为功能接口。将Java lambda表达式与功能接口匹配分为以下步骤:

  • 接口是否只有一个方法?
  • lambda表达式的参数是否与接口唯一方法的参数相匹配?
  • lambda表达式的返回值类型是否与接口唯一方法的返回值类型一致?

如果这三个问题的答案是肯定的,那么给定的lambda表达式与接口成功匹配。

Lambda类型推断

在Java8之前,当你在实现匿名接口时必须指定要实现的接口。下面是文章开头的匿名接口实现示例:

stateOwner.addStateListener(new StateChangeListener() {

    public void onStateChange(State oldState, State newState) {
        // 代码实现
    }
});

对于lambda表达式,类型通常可以从周围的代码中推断出来。例如,参数的接口类型可以从addStateListener()方法的方法声明(StateChangeListener接口上的单个方法)推断出来。这称为类型推断。编译器通过在别处查找类型来推断参数的类型 - 在这个例子中是通过方法定义来推断的。下面是本文开头的示例,显示在lambda表达式中未提及StateChangeListener接口:

stateOwner.addStateListener(
    (oldState, newState) -> System.out.println("State changed")
);

在lambda表达式中,通常也可以推断参数类型。在上面的例子中,编译器可以从onStateChange()方法声明中推断出它们的类型。因此,参数oldState和newState的类型是从onStateChange()方法的方法声明中推断出来的。

lambda参数

由于Java lambda表达式实际上只是方法,因此lambda表达式可以像方法一样使用参数。前面显示的lambda表达式的(oldState,newState)部分指定了lambda表达式所需的参数。这些参数必须与单个方法接口上方法的参数相匹配。在这个例子中,这些参数必须与StateChangeListener接口的onStateChange()方法的参数匹配:

public void onStateChange(State oldState, State newState);

至少需要保证lambda表达式和方法中的参数数目必须匹配。

其次,如果你在lambda表达式中指定了任何参数类型,则这些类型也必须匹配。

零个参数

如果lambda表达式匹配的方法不带任何参数,那么你可以像这样写lambda表达式:

() -> System.out.println("Zero parameter lambda");

注意括号中间没有内容。这是表示lambda不需要参数。

一个参数

如果Java lambda表达式匹配的方法需要一个参数,则可以像这样编写lambda表达式:

(param) -> System.out.println("One parameter: " + param);

注意参数在括号内列出。

当lambda表达式采用单个参数时,也可以省略括号,如下所示:

param -> System.out.println("One parameter: " + param);

多参数

如果lambda表达式匹配的方法需要多个参数,则参数需要在括号内列出。示例如下:

(p1, p2) -> System.out.println("Multiple parameters: " + p1 + ", " + p2);

只有当方法采用单个参数时,可以省略括号。

参数类型

如果编译器无法从lambda匹配的函数接口方法中推断出参数类型,那么为lambda表达式指定参数类型有时可能是必须的。别担心,编译器会告诉你什么时候是这样。这是一个Java lambda参数类型的例子:

(Car car) -> System.out.println("The car is: " + car.getName());

正如你所看到的,就像你在其他地方的方法中声明一个参数时,或者当做一个接口的匿名实现一样,car参数的类型(Car)被写在参数名称本身的前面。

lambda函数主体

lambda表达式的主体,以及它所表示的函数/方法的主体,在lambda声明中的 - >的右侧被指定:下面是一个例子:

(oldState, newState) -> System.out.println("State changed")

如果你的lambda表达式需要由多行组成,那么你可以将lambda函数体包含在{}括号中,Java也需要在其他地方声明方法。下面是一个例子:

(oldState, newState) -> {
    System.out.println("Old state: " + oldState);
    System.out.println("New state: " + newState);
  }

Lambda表达式返回值

你可以从Java lambda表达式中返回值,就像你可以从方法中返回一样。你只需要将一个return语句添加到lambda函数体中,就像这样:

(param) -> {
    System.out.println("param: " + param);
    return "return value";
  }

如果你的lambda表达式只是计算一个返回值并返回它,你可以用更短的方式指定返回值。不需要像下面这样:

(a1, a2) -> { return a1 > a2; }

而是这样:

(a1, a2) -> a1 > a2;

编译器会发现表达式a1> a2是lambda表达式的返回值

Lambda作为一个对象

Java lambda表达式本质上是一个对象。你可以将一个lambda表达式分配给一个变量并将其传递。下面里是一个例子:

public interface MyComparator {

    public boolean compare(int a1, int a2);

}
MyComparator myComparator = (a1, a2) -> return a1 > a2;

boolean result = myComparator.compare(2, 5);

第一个代码块显示了lambda表达式实现的接口。第二个代码块显示了lambda表达式的定义,lambda表达式如何分配给变量,最后是如何通过调用它实现的接口方法调用lambda表达式。

下一篇:练习

results matching ""

    No results matching ""