文章标题
- 什么是lambda表达式
- Lambda表达式示例
- Lambda表达式特点
- 更多实战案例
- 场景1:使用 lambda 表达式迭代列表并对列表项执行某些操作
- 场景2:使用 lambda 表达式在 Java 中创建并启动线程
- 场景3:使用 lambda 表达式向 GUI 组件添加事件监听器
- Lambda表达式的优点
- 总结
Lambda表达式在其他编程语言中经常见到,比如Scala、Python等,lambda其实就是数学中的字符λ,表示的就是函数抽象。
什么是lambda表达式
在一般的编程语言中,Lambda 表达式是一个匿名函数,即没有任何方法名称或标识符,只带有形式参数列表和函数体,参数列表和函数体之间用箭头(->)分隔。
**在Java中lambda表达式是表示函数式接口实例的表达式。**函数式接口的使用参考:【Java函数篇】Java8中函数接口Function使用详解
与 Java 中的其他类型类似,lambda 表达式也是有类型的,其类型是函数式接口类型。为了推断类型,编译器会查看 lambda 表达式中赋值语句的左侧。
lambda 表达式本身并不包含它正在实现哪个函数接口的信息。此信息是根据表达式的使用上下文推断出来的。
Lambda表达式示例
一个典型的 lambda 表达式语法如下:
(parameters) -> expression
例如,下面给出的 lambda 表达式接受两个参数并返回它们的和。根据 x 和 y 的类型,表达式的使用方式会有所不同。
- If the parameters match to Integer the expression will add the two numbers.
- If the parameters of type String the expression will concat the two strings.
(x, y) -> x + y
例如,我们有以下函数接口 Operator。它有一个方法 process(),该方法接受两个参数并返回一个值。
@FunctionalInterface
interface Operator<T> {T process(T a, T b);
}
也可以按照以下方式为此功能接口创建 lambda 表达式。请注意,我们可以创建方法实现并立即使用它们。我们不需要创建实现 Operator 接口的具体类 OperatorImpl。
Operator<Integer> addOperation = (a, b) -> a + b;
System.out.println(addOperation.process(3, 3)); //Prints 6
Operator<String> appendOperation = (a, b) -> a + b;
System.out.println(appendOperation.process("3", "3")); //Prints 33
Operator<Integer> multiplyOperation = (a, b) -> a * b;
System.out.println(multiplyOperation.process(3, 3)); //Prints 9
功能接口类型的两个很好的例子是 Consumer 和 BiConsumer 接口,它们在 Stream API 中被广泛用于创建 lambda 表达式。
Lambda表达式特点
lambda表达式可以有0、1、2、3等多个参数
(x, y) -> x + y
(x, y, z) -> x + y + z
lambda 表达式的主体可以包含零个、一个或多个语句。如果 lambda 表达式的主体只有一个语句,则大括号不是必需的,并且匿名函数的返回类型与主体表达式的返回类型相同。当主体中有多个语句时,这些语句必须用大括号括起来。
(parameters) -> { statements; }
参数的类型可以明确声明,也可以从上下文中推断出来。在上例中,addOperation 和 appendOperation 的类型是从上下文中派生出来的。
多个参数必须用括号括起来,并用逗号分隔。空括号用于表示一组空的参数。
() -> expression
当只有一个参数时,如果可以推断其类型,则不必使用括号。
a -> return a * a;
- Lambda 表达式不能有 throws 子句。这是从其使用上下文和其主体推断出来的。
- Lambda 表达式不能是泛型的,即它们不能声明类型参数
更多实战案例
下面列了一些Lambda表达式实际使用的场景和案例,你可以通过学习了解怎么去使用。
场景1:使用 lambda 表达式迭代列表并对列表项执行某些操作
在给定的示例中,我们遍历列表并在标准输出中打印所有列表元素。我们可以执行任何所需的操作来代替打印它们。
List<String> pointList = new ArrayList();
pointList.add("1");
pointList.add("2");
pointList.forEach( p -> { System.out.println(p); } );
场景2:使用 lambda 表达式在 Java 中创建并启动线程
在给定的示例中,我们将 Runnable 接口的实例传递到 Thread 构造函数中。
new Thread(() -> System.out.println("My Runnable");
).start();
还记得在jdk7中是如何创建线程的吗:
// JDK7 匿名内部类写法
new Thread(new Runnable(){// 接口名@Overridepublic void run(){// 方法名System.out.println("Thread run()");}
}).start();
lambda表达式跟匿名内部类的作用是一样的,但比匿名内部类更进一步。这里连接口名和函数名都一同省掉了,写起来更加简洁。如果函数体有多行,可以用大括号括起来,就像这样:
// JDK8 Lambda表达式代码块写法
new Thread(() -> {System.out.print("Hello");System.out.println(" Hoolee");}
).start();
场景3:使用 lambda 表达式向 GUI 组件添加事件监听器
JButton button = new JButton("Submit");
button.addActionListener((e) -> {System.out.println("Click event triggered !!");
});
以上是 java 8 中 lambda 表达式的非常基本的示例,接下来继续看看Lambda表达式的高级使用技巧。
Lambda表达式的优点
Lambda 表达式为 Java 带来了许多函数式编程的优势。与大多数 面相对象编程 语言一样,Java 是围绕类和对象构建的,并且只将类视为其一等公民。其他重要的编程实体(例如函数)则处于次要地位。
但在函数式编程中,我们可以定义函数,为其提供引用变量,并将它们作为方法参数传递等等。JavaScript 是函数式编程的一个很好的例子,我们可以在其中将回调方法传递给 Ajax 调用等等。
请注意,在 Java 8 之前,我们可以使用匿名类完成使用 lambda 表达式可以完成的所有操作,但它们使用非常简洁的语法来实现相同的结果。让我们看看使用这两种技术对相同方法实现的比较。
// 使用Lambda表达式
Operator<Integer> addOperation = (a, b) -> a + b;// 使用匿名内部类
Operator<Integer> addOperation = new Operator<Integer>() {@Overridepublic Integer process(Integer a, Integer b) {return a + b;}
};
总结
Lambda 表达式是一个非常有用的特性,并且从一开始就是 Java 所缺乏的。现在有了 Java 8,我们也可以借助它来使用函数式编程概念。