# Lambda 表达式简介

> 原文： [https://javabeginnerstutorial.com/core-java-tutorial/introduction-lambda-expressions/](https://javabeginnerstutorial.com/core-java-tutorial/introduction-lambda-expressions/)

在本文中，我将向您简要介绍 Java 8 的一项新功能：lambda 表达式。 我将向您展示什么是 lambda 表达式以及如何利用这一新功能。

## 什么是 lambda 表达式？

Lambda 表达式类似于其他编程语言中的闭包-或 Java 本身中的匿名函数或内部类。

Lambda 表达式希望利用匿名类的用法。 您可能还记得将操作监听器注册到图形用户界面中的按钮的方式：

```java
JButton clickMeButton = new JButton("Click me!");
clickMeButton.addActionListener(new ActionListener(){

    @Override public void actionPerformed(ActionEvent event){
        System.out.println("You clicked me!");
    }
});
```

是的，上面的代码向您展示了一个匿名内部类，该类仅用于实现`ActionListener`接口的唯一方法`actionPerformed(ActionEvent)`。 而且这很笨拙—即使现代的 IDE 可以帮助您完成代码填充以填充方法的主体。

但是，使用 lambda 表达式，您可以像下面这样简单地实现此方法：

```java
JButton clickMeButton = new JButton("Click me!");
clickMeButton.addActionListener(event -> System.out.println("You clicked me!"));
```

这样好多了，不是吗？ 现在该深入了解如何构建自己的 lambda 表达式了。

Lambda 表达式具有以下语法：

```java
parameters -> expression body
```

lambda 表达式的关键是`->`箭头符号。

让我们看一个基本的示例：

```java
(int x) -> { return x * x; };
```

还有其他一些特征可以简化 lambda 表达式的使用：

*   如果仅需要一个参数，则括号在参数周围是可选的
*   参数的类型声明是可选的
*   `return`语句是可选的
*   表达式主体周围的花括号是可选的—如果表达式主体包含单个语句

因为我们的简单表达式符合上述所有四个要点，所以我们可以像这样覆盖 lambda 表达式：

```java
x -> x * x;
```

这使事情更具可读性。

## 函数式接口

Lambda 表达式可用于实现所谓的**函数式接口**的方法。 较新的接口可以带有注解`@FunctionalInterface`，以指示此接口是函数式接口-但是，每个接口都可以视为仅具有**一个抽象方法**的函数式接口。因此，即使接口没有被非正式地标记为`FunctionalInterface`，也可以使用 lambda 表达式替换/实现`ActionListener`的`addActionListener`。

默认方法不具有抽象性，因为它们具有实现。 如果接口在不提供实现的情况下覆盖了`java.lang.Object`的方法，则该实现也不会被视为抽象，因为该实现将继承`java.lang.Object`类的实现。 让我们来看一个示例：

```java
/**
 * @author GHajba
 *
 */
public interface ExampleFunctionalInterface {

    /**
     * @return a name
     */
    String getName();

    /**
     * prints a greeting
     */
    default void printGreeting() {
        System.out.println("Hello " + getName() + "!");
    }

    /**
     *
     * @return the string representation of this object
     */
    @Override
    String toString();
}
```

即使我们不添加注解，上面的接口也是一个函数式接口。 现在，我们创建一个 lambda 并使用它。 请记住，我们的 lambda 必须仅实现`getName()`函数，该函数不需要任何参数并返回`String`：

```java
final ExampleFunctionalInterface example = () -> "GHajba"; // implements the getName() function
example.printGreeting();
System.out.println(example); // this calls example.toString() implicitly
```

如果运行示例，我们将得到与以下结果类似的结果：

```java
Hello GHajba!
ChangeCalculator$$Lambda$1/[[email protected]](/cdn-cgi/l/email-protection)
```

如您所见，默认方法可以与实现的 lambda 一起正常工作，并且可以从`java.lang.Object`调用`toString`，因为不存在 lambda 表达式中的直接实现。

## 更多示例

了解了 lambda 表达式后，我们来看一些示例，在这些示例中我们可以使用它们来改进现有或新代码。

因为 lambda 表达式是匿名函数或内部类，所以我们可以用它们替换对现有匿名函数的调用。 就像 GUI 应用的动作监听器或`Runnable`接口的本地实现一样。

```java
final Runnable runnable = new Runnable() {

    @Override
    public void run() {
        System.out.println("Hello lambdas?");
    }
};

runnable.run();
```

上面的示例使用匿名类实现`Runnable`接口。 现在来看看这个简单的“你好 lambdas”吗？ 输出这是要做的一些工作。

```java
final Runnable runnable_lambdas = () -> System.out.println("Hello lambdas!");
runnable_lambdas.run();
```

上面的代码示例使用 lambda 表达式，并且做完全相同的事情：它输出字符串“`Hello lambdas!`”。 到控制台-但是无需创建匿名类来实现`Runnable`接口。 自然，此代码仅适用于函数式接口（只有一种方法的接口）。

如您所见，也可以创建无参数的 lambda。 为此，您需要提供空括号作为参数，以表明没有任何内容。

除此之外，lambda 表达式也是闭包。 这意味着它们是可以保存在变量中并在以后重用的函数。 如果我们看一下上一节中的示例，我们可以将其保存到一个名为`square`的变量中，并在以后重用：

```java
IntUnaryOperator square = x -> x * x;
```

要使用它，我们可以直接调用它，也可以在某些流表达式中使用它，例如`map`：

```java
System.out.println(square.applyAsInt(128));
System.out.println("------");
IntStream.rangeClosed(1, 10).map(square).forEach(System.out::println);
```

上面的代码片段的结果是这样的：

```java
16384
------
1
4
9
16
25
36
49
64
81
100
```

## 柯里化

柯里化是函数部分应用的名称。 这就是说，您有一个函数，该函数需要多个参数，但是大多数参数已经为人所知，并且将保持不变-只有一个会发生变化。 而且，除了在调用函数时提供每个参数之外，还可以部分咖喱函数并设置一些默认参数。

让我们看一下以下示例：

```java
public static Function<Integer, Function<Integer, Integer>> multiply() {
    return x -> y -> x * y;
}
```

这段代码创建了一个简单的函数，将两个`Integer`类型的变量相乘。 因为返回的函数需要两个参数（x 和 y），所以我们可以创建这些函数的当前版本并在以后使用它们：

```java
final Function<Integer, Integer> two_times = multiply().apply(2);
final Function<Integer, Integer> three_times = multiply().apply(3);
```

现在，我们对每个函数都应用了一个（变量）变量。 如果调用这些函数（应用第二个变量），我们可以看到结果表示已经应用的值：

```java
System.out.println(two_times.apply(3)); // prints 6
System.out.println(three_times.apply(3)); // prints 9
```

每次自然地调用`apply()`有点麻烦，但是 Java 目前无法使其变得更好。 如果可以编写以下代码，则将是更好的语法：

```java
final Function<Integer, Integer> two_times = multiply()(2)
System.out.println(two_times(3));
```

这目前尚不起作用，但是添加到 Java 语言中将是一个不错的语法糖。

## 总结

如果您要实现函数式接口而无需创建和实例化实现该接口的类，那么 Lambda 表达式功能强大-主要是因为您只需要一次（例如，用作按钮的`ActionListener`）。

我们研究了珂里化/函数部分应用，并讨论了一些功能将使其更易于在 Java 中使用，并希望在将来的版本中我们可以使用它。