知识模块
☕ Java 知识模块
六、Java 新特性
Lambda 表达式

Lambda 表达式

面试提问

"Lambda 表达式是什么?函数式接口有哪些?"


Lambda 表达式简介

Lambda 表达式是 Java 8 引入的重要特性,允许将函数作为方法参数传递。

语法

// 基本语法
(参数列表) -> { 方法体 }
 
// 示例
(String s) -> s.length()
(int x, int y) -> x + y
() -> 42

对比传统写法

// 传统匿名内部类
Runnable r1 = new Runnable() {
    @Override
    public void run() {
        System.out.println("Hello");
    }
};
 
// Lambda 表达式
Runnable r2 = () -> System.out.println("Hello");
 
// 传统写法
Comparator<String> c1 = new Comparator<String>() {
    @Override
    public int compare(String a, String b) {
        return a.length() - b.length();
    }
};
 
// Lambda 表达式
Comparator<String> c2 = (a, b) -> a.length() - b.length();

语法精简规则

1. 参数类型可省略

// 完整写法
(String s) -> s.length()
 
// 省略类型
s -> s.length()

2. 单参数可省略括号

// 完整写法
(s) -> s.length()
 
// 省略括号
s -> s.length()

3. 方法体单语句可省略花括号和 return

// 完整写法
s -> { return s.length(); }
 
// 省略写法
s -> s.length()

函数式接口

定义

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

@FunctionalInterface
public interface Runnable {
    void run();
}

Java 内置函数式接口

接口参数返回用途
Runnablevoid执行操作
SupplierT供给数据
ConsumerTvoid消费数据
Function<T,R>TR转换数据
PredicateTboolean判断条件
BiFunction<T,U,R>T, UR双参数转换
BinaryOperatorT, TT双参数同类型运算
UnaryOperatorTT单参数同类型运算

使用示例

// Supplier:无参有返回
Supplier<String> supplier = () -> "Hello";
String s = supplier.get();  // "Hello"
 
// Consumer:有参无返回
Consumer<String> consumer = str -> System.out.println(str);
consumer.accept("World");  // 打印 "World"
 
// Function:有参有返回
Function<String, Integer> function = str -> str.length();
int len = function.apply("Hello");  // 5
 
// Predicate:有参返回 boolean
Predicate<String> predicate = str -> str.length() > 3;
boolean result = predicate.test("Hello");  // true
 
// BinaryOperator:两个同类型参数返回同类型
BinaryOperator<Integer> add = (a, b) -> a + b;
int sum = add.apply(1, 2);  // 3

方法引用

方法引用是 Lambda 表达式的简写形式,使用 :: 操作符。

四种形式

类型语法示例
静态方法引用类名::静态方法Math::abs
实例方法引用对象::实例方法System.out::println
类型方法引用类名::实例方法String::length
构造器引用类名::newArrayList::new

示例

// 静态方法引用
Function<Integer, Integer> f1 = x -> Math.abs(x);
Function<Integer, Integer> f2 = Math::abs;
 
// 实例方法引用
Consumer<String> c1 = s -> System.out.println(s);
Consumer<String> c2 = System.out::println;
 
// 类型方法引用(第一个参数作为调用者)
Function<String, Integer> f3 = s -> s.length();
Function<String, Integer> f4 = String::length;
 
// 构造器引用
Supplier<List<String>> s1 = () -> new ArrayList<>();
Supplier<List<String>> s2 = ArrayList::new;
 
// 带参数的构造器引用
Function<Integer, List<String>> f5 = n -> new ArrayList<>(n);
Function<Integer, List<String>> f6 = ArrayList::new;

Lambda 与变量作用域

访问局部变量

Lambda 可以访问外部的局部变量,但必须是 effectively final(事实上不可变)。

String prefix = "Hello, ";  // effectively final
 
Consumer<String> consumer = name -> {
    System.out.println(prefix + name);  // 可以访问
    // prefix = "Hi, ";  // 编译错误:不能修改
};
 
// prefix = "Hi, ";  // 编译错误:修改后 Lambda 无法访问

this 引用

Lambda 内部的 this 指向外部类,而匿名内部类的 this 指向匿名类本身。

public class Test {
    public void run() {
        // Lambda:this 指向 Test
        Runnable r1 = () -> System.out.println(this.getClass());  // Test
        
        // 匿名内部类:this 指向匿名类
        Runnable r2 = new Runnable() {
            @Override
            public void run() {
                System.out.println(this.getClass());  // Test$1
            }
        };
    }
}

实际应用

集合排序

List<String> list = Arrays.asList("banana", "apple", "cherry");
 
// 传统写法
Collections.sort(list, new Comparator<String>() {
    @Override
    public int compare(String a, String b) {
        return a.compareTo(b);
    }
});
 
// Lambda
Collections.sort(list, (a, b) -> a.compareTo(b));
 
// 方法引用
Collections.sort(list, String::compareTo);
 
// List.sort()
list.sort(String::compareTo);

集合遍历

List<String> list = Arrays.asList("a", "b", "c");
 
// 传统 for 循环
for (String s : list) {
    System.out.println(s);
}
 
// Lambda
list.forEach(s -> System.out.println(s));
 
// 方法引用
list.forEach(System.out::println);

条件过滤

List<Integer> numbers = Arrays.asList(1, 2, 3, 4, 5, 6);
 
// 过滤偶数
List<Integer> evens = numbers.stream()
    .filter(n -> n % 2 == 0)
    .collect(Collectors.toList());
// [2, 4, 6]

Lambda vs 匿名内部类

对比项Lambda匿名内部类
代码简洁度简洁冗长
this 指向外部类匿名类本身
可访问变量effectively finaleffectively final
可实现接口仅函数式接口任意接口
可继承类不可以可以(抽象类)
编译产物invokedynamic生成类文件

面试要点总结

问题答案要点
Lambda 是什么?匿名函数,可传递的代码块
函数式接口?只有一个抽象方法的接口
常用函数式接口?Supplier、Consumer、Function、Predicate
方法引用形式?静态方法、实例方法、类型方法、构造器
变量访问限制?外部变量必须是 effectively final

参考资料