Lambda 表达式语法
典型的 lambda 表达式语法如下所示:
(parameters) -> expression
例如,给定的 lambda 表达式接受两个参数并返回它们的相加。
根据 x
和 y
的类型,表达式的使用方式会有所不同。
如果参数与 Integer
匹配,则表达式会将两个数字相加。
如果参数类型为String
,则表达式将连接两个字符串。
(x, y) -> x + y
Java 8 Lambda 表达式的特点
- 一个 lambda 表达式可以有零个、一个或者多个参数。
- 参数的类型可以显式声明,也可以从上下文中推断出来。
- 多个参数用强制括号括起来并用逗号分隔。空括号用于表示一组空参数。
() -> expression (param1, param2, param3) -> expression
- 当有单个参数时,如果推断其类型,则不强制使用括号。例如 a -> 返回 a*a。
- lambda 表达式的主体可以包含零个、一个或者多个语句。
- 如果 lambda 表达式的主体有单个语句,则大括号不是必需的,并且匿名函数的返回类型与主体表达式的返回类型相同。当正文中有多个语句时,必须将这些语句括在大括号中。
(parameters) -> { statements; }
因此,我们简要概述了什么是 lambda 表达式。
在深入研究 lambda 表达式和 Java 编程之间的关系之前,我们还必须了解函数式接口。
Java 8 的函数式接口(functional interface)
单一抽象方法接口(SAM 接口 - Single Abstract Method interfaces)并不是一个新概念。
这意味着只有一种方法的接口。
在 Java 中,我们已经有很多此类 SAM 接口的示例。
从 Java 8 开始,SAM 接口也将被称为功能接口。
Java 8 通过使用新注解标记这些接口来强制执行单一职责规则,例如:@FunctionalInterface。
例如,Runnable 接口的新定义是这样的:
@FunctionalInterface public interface Runnable { public abstract void run(); }
如果我们尝试在任何函数式接口中添加新方法,编译器将不允许我们这样做,并且会抛出编译时错误。
我们知道 Lambda 表达式是没有名称的匿名函数,它们(主要)作为参数传递给其他函数。
好吧,在 Java 中,方法参数总是有一个类型,并且会查找类型信息以确定在方法重载甚至简单方法调用的情况下需要调用哪个方法。
因此,基本上每个 lambda 表达式也必须可以转换为某种“类型”才能被接受为方法参数。
好吧,转换 lambda 表达式的类型始终是函数式接口类型。
函数式接口类型的两个很好的例子是 Consumer 和 BiConsumer 接口。
让我们通过一个例子来理解它。
如果我们必须编写一个将在控制台中打印“onitroad”的线程,那么最简单的代码将是:
new Thread(new Runnable() { @Override public void run() { System.out.println("onitroad"); } }).start();
如果我们为此任务使用 lambda 表达式,则代码将是:
new Thread( () -> { System.out.println("My Runnable"); } ).start();
Runnable
是一个具有单一方法 run()
的函数式接口。
因此,当我们将 lambda 表达式传递给 Thread
类的构造函数时,编译器会尝试将表达式转换为等效的 Runnable
代码,如第一个代码示例所示。
如果编译器成功,则一切正常。
如果编译器无法将表达式转换为等效的实现代码,它就会报错。
在这里,在上面的示例中,lambda 表达式被转换为类型 Runnable
。
lambda 表达式是函数式接口的一个实例,但 lambda 表达式本身不包含有关它正在实现的函数式接口的信息。
该信息是从使用表达式的上下文中推导出来的。
Java 8 Lambda 表达式示例
我们列出了一些代码示例,我们可以阅读并分析如何在我们的日常编程中使用 lambda 表达式。
示例 1:使用 lambda 表达式迭代 List 并对列表元素执行某个操作
在给定的示例中,我们迭代列表并打印标准输出中的所有列表元素。
我们可以执行任何所需的操作来代替打印它们。
List<String> pointList = new ArrayList(); pointList.add("1"); pointList.add("2"); pointList.forEach(p -> { System.out.println(p); } );
示例 2:在 Java 中使用 lambda 表达式创建和启动线程
在给定的示例中,我们将 Runnable
接口的实例传递给 Thread
构造函数。
new Thread( () -> System.out.println("My Runnable"); ).start();
示例 3:使用 lambda 表达式向 GUI 组件添加事件侦听器
JButton button = new JButton("Submit"); button.addActionListener((e) -> { System.out.println("Click event triggered !!"); });
Lambda 表达式是 从Java 8 开始引入的新特性。
1. 什么是 Lambda 表达式?
在一般编程语言中,Lambda 表达式(或者函数)是匿名函数,即没有名称和任何标识符的函数。
Lambda 表达式是作为常量值给出的无名函数,并准确地写在需要它的地方,通常作为其他函数的参数。
Lambda 表达式最重要的特性是它们在其外观的上下文中执行。
因此,类似的 lambda 表达式可以在其他一些上下文中以不同的方式执行(即逻辑将相同但结果将根据传递给函数的不同参数而不同)。
上面的定义充满了关键字,只有当你对什么是 lambda 表达式有一点了解时才能理解。
与面向对象编程 (OOP) 相比,Lambda 表达式为函数式编程提供了许多好处。
大多数 OOP 语言围绕类和对象发展,这些语言只将类视为它们的一等公民。
另一个重要的实体,例如:职能部门,则处于次要地位。
在 Java 中尤其如此,其中函数不能存在于对象之外。
函数本身在 Java 中没有任何意义,除非它与某个对象或者实例相关。
但是在函数式编程中,我们可以定义函数,给它们提供引用变量,并将它们作为方法参数传递等等。
JavaScript 是函数式编程的一个很好的例子,我们可以将回调方法传递给 Ajax 调用等等。
这是一个非常有用的特性,从一开始在Java没有。
现在有了 Java 8,我们还可以在 lambda 表达式的帮助下使用函数式编程概念。