Lambda 表达式简介
原文: https://javabeginnerstutorial.com/core-java-tutorial/introduction-lambda-expressions/
在本文中,我将向您简要介绍 Java 8 的一项新功能:lambda 表达式。 我将向您展示什么是 lambda 表达式以及如何利用这一新功能。
什么是 lambda 表达式?
Lambda 表达式类似于其他编程语言中的闭包-或 Java 本身中的匿名函数或内部类。
Lambda 表达式希望利用匿名类的用法。 您可能还记得将操作监听器注册到图形用户界面中的按钮的方式:
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 表达式,您可以像下面这样简单地实现此方法:
JButton clickMeButton = new JButton("Click me!");
clickMeButton.addActionListener(event -> System.out.println("You clicked me!"));
这样好多了,不是吗? 现在该深入了解如何构建自己的 lambda 表达式了。
Lambda 表达式具有以下语法:
parameters -> expression body
lambda 表达式的关键是->
箭头符号。
让我们看一个基本的示例:
(int x) -> { return x * x; };
还有其他一些特征可以简化 lambda 表达式的使用:
- 如果仅需要一个参数,则括号在参数周围是可选的
- 参数的类型声明是可选的
return
语句是可选的- 表达式主体周围的花括号是可选的—如果表达式主体包含单个语句
因为我们的简单表达式符合上述所有四个要点,所以我们可以像这样覆盖 lambda 表达式:
x -> x * x;
这使事情更具可读性。
函数式接口
Lambda 表达式可用于实现所谓的函数式接口的方法。 较新的接口可以带有注解@FunctionalInterface
,以指示此接口是函数式接口-但是,每个接口都可以视为仅具有一个抽象方法的函数式接口。因此,即使接口没有被非正式地标记为FunctionalInterface
,也可以使用 lambda 表达式替换/实现ActionListener
的addActionListener
。
默认方法不具有抽象性,因为它们具有实现。 如果接口在不提供实现的情况下覆盖了java.lang.Object
的方法,则该实现也不会被视为抽象,因为该实现将继承java.lang.Object
类的实现。 让我们来看一个示例:
/**
* @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
:
final ExampleFunctionalInterface example = () -> "GHajba"; // implements the getName() function
example.printGreeting();
System.out.println(example); // this calls example.toString() implicitly
如果运行示例,我们将得到与以下结果类似的结果:
Hello GHajba!
ChangeCalculator$$Lambda$1/[[email protected]](/cdn-cgi/l/email-protection)
如您所见,默认方法可以与实现的 lambda 一起正常工作,并且可以从java.lang.Object
调用toString
,因为不存在 lambda 表达式中的直接实现。
更多示例
了解了 lambda 表达式后,我们来看一些示例,在这些示例中我们可以使用它们来改进现有或新代码。
因为 lambda 表达式是匿名函数或内部类,所以我们可以用它们替换对现有匿名函数的调用。 就像 GUI 应用的动作监听器或Runnable
接口的本地实现一样。
final Runnable runnable = new Runnable() {
@Override
public void run() {
System.out.println("Hello lambdas?");
}
};
runnable.run();
上面的示例使用匿名类实现Runnable
接口。 现在来看看这个简单的“你好 lambdas”吗? 输出这是要做的一些工作。
final Runnable runnable_lambdas = () -> System.out.println("Hello lambdas!");
runnable_lambdas.run();
上面的代码示例使用 lambda 表达式,并且做完全相同的事情:它输出字符串“Hello lambdas!
”。 到控制台-但是无需创建匿名类来实现Runnable
接口。 自然,此代码仅适用于函数式接口(只有一种方法的接口)。
如您所见,也可以创建无参数的 lambda。 为此,您需要提供空括号作为参数,以表明没有任何内容。
除此之外,lambda 表达式也是闭包。 这意味着它们是可以保存在变量中并在以后重用的函数。 如果我们看一下上一节中的示例,我们可以将其保存到一个名为square
的变量中,并在以后重用:
IntUnaryOperator square = x -> x * x;
要使用它,我们可以直接调用它,也可以在某些流表达式中使用它,例如map
:
System.out.println(square.applyAsInt(128));
System.out.println("------");
IntStream.rangeClosed(1, 10).map(square).forEach(System.out::println);
上面的代码片段的结果是这样的:
16384
------
1
4
9
16
25
36
49
64
81
100
柯里化
柯里化是函数部分应用的名称。 这就是说,您有一个函数,该函数需要多个参数,但是大多数参数已经为人所知,并且将保持不变-只有一个会发生变化。 而且,除了在调用函数时提供每个参数之外,还可以部分咖喱函数并设置一些默认参数。
让我们看一下以下示例:
public static Function<Integer, Function<Integer, Integer>> multiply() {
return x -> y -> x * y;
}
这段代码创建了一个简单的函数,将两个Integer
类型的变量相乘。 因为返回的函数需要两个参数(x 和 y),所以我们可以创建这些函数的当前版本并在以后使用它们:
final Function<Integer, Integer> two_times = multiply().apply(2);
final Function<Integer, Integer> three_times = multiply().apply(3);
现在,我们对每个函数都应用了一个(变量)变量。 如果调用这些函数(应用第二个变量),我们可以看到结果表示已经应用的值:
System.out.println(two_times.apply(3)); // prints 6
System.out.println(three_times.apply(3)); // prints 9
每次自然地调用apply()
有点麻烦,但是 Java 目前无法使其变得更好。 如果可以编写以下代码,则将是更好的语法:
final Function<Integer, Integer> two_times = multiply()(2)
System.out.println(two_times(3));
这目前尚不起作用,但是添加到 Java 语言中将是一个不错的语法糖。
总结
如果您要实现函数式接口而无需创建和实例化实现该接口的类,那么 Lambda 表达式功能强大-主要是因为您只需要一次(例如,用作按钮的ActionListener
)。
我们研究了珂里化/函数部分应用,并讨论了一些功能将使其更易于在 Java 中使用,并希望在将来的版本中我们可以使用它。