跳到主要内容

Java Lambda 表达式

Lambda 表达式是 Java 8 引入的重要特性,它允许我们将函数作为参数传递,使代码更加简洁和灵活。

什么是 Lambda 表达式?

Lambda 表达式是一种匿名函数,它可以没有名称,可以作为参数传递或作为返回值。

语法

// 基本语法
(parameters) -> expression

// 或

(parameters) -> { statements; }

示例

// 无参数
() -> System.out.println("Hello")

// 单参数
x -> x * 2

// 多参数
(x, y) -> x + y

// 多行语句
(x, y) -> {
int sum = x + y;
return sum;
}

函数式接口

Lambda 表达式必须绑定到一个函数式接口(Functional Interface)。

什么是函数式接口?

只包含一个抽象方法的接口称为函数式接口,可以用 @FunctionalInterface 注解标记。

@FunctionalInterface
interface MyInterface {
void doSomething();

// 可以包含默认方法和静态方法
default void defaultMethod() {
System.out.println("默认方法");
}
}

常见的函数式接口

接口抽象方法说明
Runnablevoid run()无参数无返回值
Supplier<T>T get()无参数有返回值
Consumer<T>void accept(T t)有参数无返回值
Function<T,R>R apply(T t)有参数有返回值
Predicate<T>boolean test(T t)返回布尔值
BiConsumer<T,U>void accept(T t, U u)两个参数无返回值

Lambda 表达式示例

与集合结合

import java.util.Arrays;
import java.util.List;
import java.util.ArrayList;

List<String> names = Arrays.asList("Alice", "Bob", "Charlie", "Diana");

// forEach - 遍历
names.forEach(name -> System.out.println(name));
names.forEach(System.out::println);

// filter - 过滤
List<String> filtered = names.stream()
.filter(name -> name.startsWith("A"))
.collect(Collectors.toList());

// map - 转换
List<Integer> lengths = names.stream()
.map(name -> name.length())
.collect(Collectors.toList());

// sort - 排序
names.sort((a, b) -> a.compareTo(b));
names.sort(String::compareTo);

使用 Comparator

List<String> names = Arrays.asList("Charlie", "Alice", "Bob");

// 按长度排序
names.sort((a, b) -> a.length() - b.length());

// 按字母逆序排序
names.sort((a, b) -> b.compareTo(a));

// 使用 Comparator 静态方法
names.sort(Comparator.comparingInt(String::length));
names.sort(Comparator.reverseOrder());

使用 Predicate

import java.util.function.Predicate;

Predicate<String> isLong = s -> s.length() > 5;
Predicate<String> startsWithA = s -> s.startsWith("A");

// 使用 and
Predicate<String> combined = isLong.and(startsWithA);

// 使用 or
Predicate<String> either = isLong.or(startsWithA);

// 使用 negate
Predicate<String> notLong = isLong.negate();

// 应用
System.out.println(isLong.test("HelloWorld")); // true
System.out.println(either.test("Bob")); // false

使用 Function

import java.util.function.Function;

// 字符串转整数
Function<String, Integer> strToInt = s -> Integer.parseInt(s);
Integer result = strToInt.apply("123");

// 组合函数
Function<Integer, Integer> doubleValue = n -> n * 2;
Function<Integer, Integer> addTen = n -> n + 10;

// andThen: 先执行当前函数,再执行另一个函数
Function<Integer, Integer> combined = doubleValue.andThen(addTen);
System.out.println(combined.apply(5)); // 20 (5*2+10)

// compose: 先执行另一个函数,再执行当前函数
Function<Integer, Integer> composed = doubleValue.compose(addTen);
System.out.println(composed.apply(5)); // 30 (5+10)*2

使用 Supplier

import java.util.function.Supplier;

// 获取当前时间
Supplier<Long> timeSupplier = () -> System.currentTimeMillis();

// 创建对象
Supplier<Person> personSupplier = () -> new Person("张三", 20);

// 使用
System.out.println(timeSupplier.get());
Person p = personSupplier.get();

使用 Consumer

import java.util.function.Consumer;

// 打印
Consumer<String> printer = s -> System.out.println("Value: " + s);
printer.accept("Hello");

// 使用 andThen
Consumer<String> consumer1 = s -> System.out.println("Step1: " + s);
Consumer<String> consumer2 = s -> System.out.println("Step2: " + s);
Consumer<String> combined = consumer1.andThen(consumer2);
combined.accept("Test"); // 先输出 Step1: Test,再输出 Step2: Test

方法引用

方法引用是 Lambda 表达式的简化写法。

类型

// 静态方法引用
Function<String, Integer> parser = Integer::parseInt;

// 实例方法引用
String str = "Hello";
Supplier<Integer> lenSupplier = str::length;

// 构造函数引用
Supplier<ArrayList<String>> listSupplier = ArrayList::new;
Function<Integer, ArrayList<String>> listWithSize = ArrayList::new;

示例

List<String> names = Arrays.asList("Alice", "Bob", "Charlie");

// 方法引用 - System.out::println
names.forEach(System.out::println);

// 方法引用 - String::toUpperCase
names.stream()
.map(String::toUpperCase)
.collect(Collectors.toList());

// 方法引用 - compareToIgnoreCase
names.sort(String::compareToIgnoreCase);

// 构造函数引用
List<String[]> arrays = names.stream()
.map(name -> new String[]{name})
.collect(Collectors.toList());

// 等价于
List<String[]> arrays2 = names.stream()
.map(String[]::new)
.collect(Collectors.toList());

变量作用域

Lambda 表达式可以访问外部变量,但有一些限制。

访问局部变量

int count = 0;

// Lambda 可以读取局部变量
Runnable r = () -> System.out.println("Count: " + count);

// 但不能修改局部变量(会产生编译错误)
// count++; // 错误

// 使用数组来"绕过"这个限制
int[] counter = {0};
Runnable r2 = () -> counter[0]++;

访问成员变量和静态变量

class MyClass {
int instanceVar = 10;
static int staticVar = 20;

void test() {
// 可以访问和修改成员变量
Runnable r1 = () -> {
instanceVar++;
staticVar++;
};
}
}

this 关键字

在 Lambda 表达式中,this 指向创建 Lambda 的外部类的实例。

class MyClass {
int value = 100;

void test() {
Runnable r = () -> {
// 这里 this 指向 MyClass 的实例
System.out.println(this.value); // 100
};
}
}

典型使用场景

1. 线程创建

// 传统方式
new Thread(new Runnable() {
@Override
public void run() {
System.out.println("Hello");
}
}).start();

// Lambda 方式
new Thread(() -> System.out.println("Hello")).start();

// 更简洁
new Thread(() -> {
// 执行任务
}).start();

2. 事件处理

button.addActionListener(e -> System.out.println("按钮被点击"));

// 处理多个事件
KeyListener listener = e -> {
if (e.getKeyCode() == KeyEvent.VK_ENTER) {
// 处理回车键
}
};

3. 回调

interface Callback {
void onComplete(String result);
}

void asyncOperation(Callback callback) {
// 异步操作完成后调用回调
callback.onComplete("完成");
}

// 使用
asyncOperation(result -> System.out.println("结果: " + result));

4. 过滤和转换

List<Integer> numbers = Arrays.asList(1, 2, 3, 4, 5);

// 过滤偶数
List<Integer> evens = numbers.stream()
.filter(n -> n % 2 == 0)
.collect(Collectors.toList());

// 平方
List<Integer> squares = numbers.stream()
.map(n -> n * n)
.collect(Collectors.toList());

// 字符串长度大于3的
List<String> longNames = names.stream()
.filter(name -> name.length() > 3)
.collect(Collectors.toList());

与匿名类的区别

// 匿名类
Runnable anonymous = new Runnable() {
@Override
public void run() {
System.out.println("匿名类");
}
};

// Lambda 表达式
Runnable lambda = () -> System.out.println("Lambda");

主要区别

特性匿名类Lambda
语法冗长简洁
this 关键字指向匿名类本身指向外部类
编译方式编译为独立的类编译为invokedynamic
序列化可以实现序列化(需实现接口)不推荐序列化

最佳实践

1. 保持简洁

// 不好的写法
list.stream()
.filter(s -> s.startsWith("A"))
.forEach(s -> System.out.println(s));

// 好的写法
list.stream()
.filter(s -> s.startsWith("A"))
.forEach(System.out::println);

2. 使用类型推断

// 不必要地指定类型
list.stream()
.map((String s) -> s.toUpperCase())
.collect(Collectors.toList());

// 让编译器推断类型
list.stream()
.map(String::toUpperCase)
.collect(Collectors.toList());

3. 避免在 Lambda 中修改外部变量

// 不好的写法
int sum = 0;
list.forEach(s -> sum += s.length());

// 好的写法
int sum = list.stream()
.mapToInt(String::length)
.sum();

小结

本章我们学习了:

  1. Lambda 表达式基础:语法和基本用法
  2. 函数式接口:常见的函数式接口及其使用
  3. 方法引用:静态方法、实例方法、构造函数引用
  4. 变量作用域:Lambda 中访问变量
  5. 典型场景:线程、事件处理、回调、过滤转换

Lambda 表达式让 Java 具备了函数式编程能力,使代码更加简洁和 expressive。

练习

  1. 使用 Lambda 表达式对列表进行排序
  2. 使用 Predicate 过滤满足条件的元素
  3. 使用 Function 进行数据转换
  4. 使用 Stream 和 Lambda 实现数据处理
  5. 将匿名类转换为 Lambda 表达式