Java 8 中的特性 - Lambda 表达式
Java 8 Feature - Lambda Expressions
本文翻译自: Java Lambda Expressions
Lambda
Lambda 表达式是 JavaSE 8 中一个重要的新特性。它提供了一种简单明了的方式来使用表达式表示函数式接口。这对 Collection 中的操作来说是十分有用的。它有助于从集合中迭代、过滤和提取数据。
Lambda 表达式用于对函数式接口来提供具体的实现。它节省了大量的代码,因为在使用 Lambda表达式的情况下,我们不需要在实现类上重复定义接口中的方法,而只需要编写实现代码即可。
在 Java 中 Lambda 表达式被视为函数,所以编译器不会为它创建 .class 文件。
函数式接口
Lambda 表达式提供了对函数式接口的实现。仅有一个抽象方法的接口被称为函数式接口。Java 中提供了一个注解: @FunctionalInterface
,用于声明一个接口是函数式接口。
为什么使用 Lambda 表达式
- 提供函数式接口的实现方法。
- 减少代码量。
Lambda 表达式的基本语法
(args) ->
在 Java 中 Lambda 表达式由三部分组成。
- 参数列表:它可以是空的,也可以是非空的。
- 箭头符号:用于连接参数列表和表达式主体。
- 主体:包含一些执行语句。
无参形式的语法
() -> {
// 无参形式的表达式, 使用一对小括号表示空参
}
单个参数的语法
arg1 -> {
// 只有一个参数的表达式, 此时参数上的小括号可以省略
}
多个参数的语法
(arg1, arg2) -> {
// 拥有一个以上的参数, 需要将参数使用小括号包裹起来
}
让我们来看一下没有使用 Lambda 表达式的情况。这里我们不使用 Lambda 表达式来实现一个函数式接口。
没有 Lambda 的情况
interface Drawable {
void draw();
}
public class WithoutLambdaExpressionExample {
public static void main(String[] args) {
int width = 10;
// 在没有 Lambda 的情况下, 通过匿名内部类的方式来实现 Drawable 接口
Drawable d = new Drawable() {
@Override
public void draw() {
System.out.println("Drawing " + width);
}
};
d.draw();
}
}
执行这段代码, 将会输出:
Drawing 10
使用 Lambda 的情况
现在,我们通过 Lambda 表达式来实现上面的示例。
@FunctionalInterface
interface Drawable {
void draw();
}
public class LambdaExpressionExample {
public static void main(String[] args) {
int width = 10;
// 使用 Lambda 表达式
Drawable d2 = () -> {
System.out.println("Drawing " + width);
};
d2.draw();
}
}
运行结果:
Drawing 10
一个 Lambda 表达式可以拥有任意数量的参数。接下来看下面这些示例:
示例: 空参形式
interface Eatable{
String eat();
}
public class LambdaExpressionExample2{
public static void main(String[] args) {
// 没有参数
Eatable e = () -> {
return "我喜欢吃饭。";
};
System.out.println(e.eat());
}
}
运行结果:
我喜欢吃饭。
示例: 单个参数
interface Eatable{
String eat(String food);
}
public class LambdaExpressionExample3{
public static void main(String[] args) {
// 只有一个参数
Eatable e = food -> {
return "我喜欢吃" + food + "。";
};
System.out.println(e.eat("糖醋茄子"));
}
}
运行结果:
我喜欢吃糖醋茄子。
示例: 多个参数
interface Addable {
int add(int a,int b);
}
public class LambdaExpressionExample4 {
public static void main(String[] args) {
// 拥有多个参数
Addable ad1 = (a, b) -> (a + b);
System.out.println(ad1.add(10, 20));
// 也可以为参数列表指定数据类型
Addable ad2 = (int a, int b) -> (a + b);
System.out.println(ad2.add(100, 200));
}
}
运行结果:
30
300
示例: 返回值
在 Lambda 表达式中,当主体内容只有一条语句时,return
关键字和{}
是可以省略的。但是当表达式中包含多条语句时,必须使用 return
关键字指定方法的返回值 (返回值为 void 的情况除外),且多条语句必须使用 {}
包裹。
interface Addable {
int add(int a, int b);
}
public class LambdaExpressionExample5 {
public static void main(String[] args) {
// 表达式中只有一条语句时省略 return 关键字和大括号
Addable ad1 = (a, b) -> a + b;
System.out.println(ad1.add(10, 20));
// 表达式中含有多条语句时不能省略 return 关键字和大括号
Addable ad2 = (a, b) -> {
int c = a + b;
return c;
};
System.out.println(ad2.add(100, 200));
}
}
运行结果:
30
300
示例: 遍历
import java.util.*;
public class LambdaExpressionExample6 {
public static void main(String[] args) {
List<String> list = new ArrayList<>();
list.add("1");
list.add("2");
list.add("3");
// 使用 forEach 遍历列表
list.forEach(n -> System.out.println(n));
}
}
运行结果:
1
2
3
示例: 创建线程
你可以使用 Lambda 表达式来运行一个线程。在下面的示例中,我们将使用 Lambda 表达式来实现 run 方法。
public class LambdaExpressionExample7 {
public static void main(String[] args) {
// 不使用 Lambda 的情况
Runnable r1 = new Runnable() {
public void run() {
System.out.println("线程1正在运行...");
}
};
Thread t1 = new Thread(r1);
t1.start();
// 使用 Lambda 来实现 run 方法
Runnable r2 = () -> {
System.out.println("线程2正在运行...");
};
Thread t2 = new Thread(r2);
t2.start();
}
}
运行结果:
线程1正在运行...
线程2正在运行...
Lambda 表达式也可以用于对集合框架的操作上。它提供了高效且简洁的方式来遍历、过滤和获取数据。下面是一些使用 Lambda 表达式操作集合的示例。
示例: Comparator
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
class Product {
int id;
String name;
float price;
public Product(int id, String name, float price) {
this.id = id;
this.name = name;
this.price = price;
}
}
public class LambdaExpressionExample8 {
public static void main(String[] args) {
List<Product> list = new ArrayList<>();
list.add(new Product(1, "HP Laptop", 25000f));
list.add(new Product(3, "Keyboard", 300f));
list.add(new Product(2, "Dell Mouse", 150f));
System.out.println("使用 name 属性进行排序...");
// 实现 Comparator 接口
Collections.sort(list, (p1, p2) -> {
return p1.name.compareTo(p2.name);
});
for (Product p : list) {
System.out.println(p.id + " " + p.name + " " + p.price);
}
}
}
运行结果:
使用 name 属性进行排序...
2 Dell Mouse 150.0
1 HP Laptop 25000.0
3 Keyboard 300.0
示例: 过滤数据
import java.util.ArrayList;
import java.util.List;
import java.util.stream.Stream;
class Product {
int id;
String name;
float price;
public Product(int id, String name, float price) {
this.id = id;
this.name = name;
this.price = price;
}
}
public class LambdaExpressionExample9 {
public static void main(String[] args) {
List<Product> list = new ArrayList<Product>();
list.add(new Product(1, "Samsung A5", 17000f));
list.add(new Product(3, "Iphone 6S", 65000f));
list.add(new Product(2, "Sony Xperia", 25000f));
list.add(new Product(4, "Nokia Lumia", 15000f));
list.add(new Product(5, "Redmi4 ", 26000f));
list.add(new Product(6, "Lenevo Vibe", 19000f));
// 使用 Lambda 表达式来过滤出价格大于 20000 的数据
Stream<Product> filtered_data = list.stream().filter(p -> p.price > 20000);
// 使用 Lambda 表达式来遍历集合中的元素
filtered_data.forEach(
product -> System.out.println(product.name + ": " + product.price)
);
}
}
运行结果:
Iphone 6S: 65000.0
Sony Xperia: 25000.0
Redmi4 : 26000.0
通过以上示例我们可以发现:Lambda 表达式轻松的实现所有的函数式接口,无论它们是否使用了 @FunctionalInterface
注解进行声明。