Java 8 引入了 Lambda 表达式,这是一个非常强大的特性,使得函数式编程风格能够在 Java 中得到应用。Lambda 表达式的引入,极大地简化了代码的编写,特别是在集合处理、事件监听、线程执行等场景中。Lambda 表达式能够使得 Java 代码更简洁、灵活和可读。
1. Lambda 表达式基本语法
Lambda 表达式的基本语法如下:
(parameters) -> expression
或者
(parameters) -> { statements; }
解释:
(parameters)
:参数列表,可以为空、一个或多个。参数类型可以省略,编译器会自动推导。->
:箭头操作符,指示 Lambda 表达式的主体开始。expression
或{ statements; }
:Lambda 表达式的主体,如果只有一行代码,可以省略{}
。如果是多行代码,则需要用{}
包裹。
2. Lambda 表达式的例子
(1) 不带参数的 Lambda 表达式
Runnable noArgument = () -> System.out.println("Hello, Lambda!");
noArgument.run(); // 输出:Hello, Lambda!
这里没有参数,直接输出字符串 "Hello, Lambda!"
。
(2) 带一个参数的 Lambda 表达式
// 输入一个字符串,输出它的长度
Function<String, Integer> stringLength = str -> str.length();
System.out.println(stringLength.apply("Hello")); // 输出:5
这里 Function<String, Integer>
表示一个接收 String
类型参数并返回 Integer
类型的函数。Lambda 表达式 str -> str.length()
就是将输入的字符串的长度作为返回值。
(3) 带多个参数的 Lambda 表达式
BiFunction<Integer, Integer, Integer> add = (a, b) -> a + b;
System.out.println(add.apply(3, 5)); // 输出:8
BiFunction<Integer, Integer, Integer>
表示一个接收两个 Integer
类型参数并返回 Integer
类型的函数。Lambda 表达式 (a, b) -> a + b
实现了两个整数相加的功能。
3. 使用 Lambda 表达式的场景
(1) 集合操作:使用 forEach
进行遍历
List<String> list = Arrays.asList("Apple", "Banana", "Orange");
// 使用 Lambda 表达式遍历列表并打印
list.forEach(item -> System.out.println(item));
这个例子中,forEach
接收一个 Consumer
类型的参数,这里我们用 item -> System.out.println(item)
来代替传统的迭代器或者增强型 for
循环。
(2) 过滤和映射:使用 Stream
和 Lambda
Stream
是 Java 8 中非常重要的功能,它可以对集合进行一系列的操作,比如过滤、映射、排序等。
List<Integer> numbers = Arrays.asList(1, 2, 3, 4, 5, 6);
// 使用 Stream 和 Lambda 表达式进行过滤
List<Integer> evenNumbers = numbers.stream()
.filter(n -> n % 2 == 0)
.collect(Collectors.toList());
System.out.println(evenNumbers); // 输出:[2, 4, 6]
在这个例子中,filter(n -> n % 2 == 0)
是 Lambda 表达式,表示保留所有偶数。
(3) 排序:使用 Comparator
和 Lambda
List<String> names = Arrays.asList("Alice", "Bob", "Charlie", "David");
names.sort((a, b) -> a.compareTo(b));
System.out.println(names); // 输出:[Alice, Bob, Charlie, David]
这里,(a, b) -> a.compareTo(b)
是一个 Lambda 表达式,用于按字母顺序排序字符串。
4. 方法引用与 Lambda
方法引用是 Lambda 表达式的一种简写形式,它直接引用类中的方法,而不需要写出 Lambda 表达式的完整形式。它是 Lambda 表达式的语法糖。
(1) 引用静态方法
// 使用 Lambda 表达式
Consumer<String> printUpperCase = str -> System.out.println(str.toUpperCase());
// 使用方法引用
Consumer<String> printUpperCaseMethodRef = String::toUpperCase;
这两种写法做的是同一件事,都是将输入的字符串转换为大写字母。
(2) 引用实例方法
List<String> list = Arrays.asList("apple", "banana", "cherry");
// 使用 Lambda 表达式
list.forEach(str -> System.out.println(str.toUpperCase()));
// 使用方法引用
list.forEach(String::toUpperCase);
在这种情况中,String::toUpperCase
是对实例方法的引用,表示调用字符串对象的 toUpperCase
方法。
(3) 引用构造函数
// Lambda 表达式
Supplier<List<String>> listSupplier = () -> new ArrayList<>();
// 方法引用
Supplier<List<String>> listSupplierRef = ArrayList::new;
在这个例子中,ArrayList::new
是一个构造函数引用,等价于 () -> new ArrayList<>()
。
5. Lambda 表达式与接口
Lambda 表达式通常用于实现函数式接口。函数式接口是只包含一个抽象方法的接口。Java 8 中通过 @FunctionalInterface
注解标记一个接口为函数式接口,虽然这个注解不是必须的,但它能帮助我们避免接口中有多个抽象方法。
(1) 定义函数式接口
@FunctionalInterface
public interface MyFunction {
int apply(int a, int b);
}
(2) 使用 Lambda 表达式实现函数式接口
MyFunction add = (a, b) -> a + b;
System.out.println(add.apply(3, 5)); // 输出:8
6. Java 8 中 Lambda 表达式常用的内置函数式接口
Java 8 提供了很多常用的内置函数式接口,它们都在 java.util.function
包中。以下是一些常用接口及其功能:
Predicate<T>
:接收一个参数并返回boolean
值,常用于过滤。Function<T, R>
:接收一个参数并返回一个结果。Consumer<T>
:接收一个参数并返回void
,常用于消费操作。Supplier<T>
:不接受参数,返回一个结果。UnaryOperator<T>
:接收一个参数并返回同类型的结果(Function
的子接口)。BinaryOperator<T>
:接收两个相同类型的参数并返回同类型的结果(BiFunction
的子接口)。
例如,使用 Predicate
接口进行过滤:
List<Integer> numbers = Arrays.asList(1, 2, 3, 4, 5, 6);
// 使用 Predicate 过滤偶数
Predicate<Integer> isEven = n -> n % 2 == 0;
numbers.stream().filter(isEven).forEach(System.out::println);
7. Lambda 表达式的优势
- 简洁性:Lambda 表达式能够使代码变得更简洁和易读,特别是在处理集合时。
- 可读性:通过 Lambda 可以清晰地表达业务逻辑,避免了匿名内部类的冗长代码。
- 函数式编程支持:使得 Java 支持函数式编程,能更好地处理集合、事件等异步场景。
- 并行处理:与
Stream
API 配合使用,Lambda 可以使得并行处理变得更加简单。
8. 总结
Lambda 表达式是 Java 8 引入的重要特性,它能够大大简化代码的编写。通过 Lambda 表达式,可以实现更加简洁和灵活的代码结构,尤其是在集合处理、事件监听、流处理等场景中。掌握 Lambda 表达式的使用,不仅能够提高代码的可读性,还能利用 Java 中的新特性(如 Stream
API)进行更高效的数据处理。