关于匿名内部类的详细知识点,请查阅《【java面向对象进阶】------内部类》
关于Array的排序规则请参考本文《【java常用API】----- Arrays》
1. 什么是函数式编程
在正式学习Lambda之前,我们需要先理解一种编程思想:函数式编程(Functional Programming)。
- 面向对象思想:强调"谁去做"。做事情必须先找一个对象,通过对象调用方法来完成。
- 函数式思想:强调"做什么"。它忽略面向对象的复杂语法,只关注结果,不关注执行的对象是谁。
Lambda表达式正是函数式思想在Java中的具体体现。
2. Lambda表达式的标准格式
Lambda表达式是JDK 8引入的一种新语法形式,它的核心作用是简化匿名内部类的写法。
2.1 标准语法结构
Lambda表达式的标准格式由三个部分组成:
java
(参数列表) -> { 方法体代码 }
():对应着接口中抽象方法的形参列表。->:Lambda操作符(箭头操作符),是固定格式,表示指向。{}:对应着接口中抽象方法的方法体。
2.2 代码演变:从匿名内部类到Lambda
让我们通过Arrays.sort的降序排序案例,看看代码是如何演变的。
需求:对整数数组进行降序排序。
2.2.1 传统写法:匿名内部类
java
Integer[] arr = {3, 1, 4, 2};
Arrays.sort(arr, new Comparator<Integer>() {
@Override
public int compare(Integer o1, Integer o2) {
return o2 - o1;
}
});
2.2.2 Lambda写法:简化后的代码
java
Integer[] arr = {3, 1, 4, 2};
// Lambda完整格式
Arrays.sort(arr, (Integer o1, Integer o2) -> {
return o2 - o1;
});
对比分析:
- 我们省略了
new Comparator<>() { ... }这一大串模板代码。 - 我们只保留了最核心的参数
(o1, o2)和执行逻辑-> { return o2 - o1; }。
3. Lambda表达式的前提条件
并不是所有的匿名内部类都能用Lambda表达式简化,必须满足以下前提:
- 必须是接口的匿名内部类。
- 接口中只能有一个抽象方法。
满足上述条件的接口,被称为函数式接口。
💡 知识点:@FunctionalInterface
这是一个注解,用于检测一个接口是否是函数式接口。如果接口上有此注解,但包含多个抽象方法,编译器会报错。虽然不写这个注解也能运行,但为了代码的严谨性,建议在函数式接口上加上此注解。
4. Lambda表达式的省略写法
Lambda之所以流行,是因为它支持大量的语法省略。其核心原则只有一条:可推导,可省略。即编译器能根据上下文推导出类型的,都可以省略。
4.1 省略规则速查表
| 规则描述 | 示例(假设接口方法为 void run() 或 int compare(...)) |
|---|---|
| 参数类型可省略 | (Integer o1, Integer o2) → (o1, o2)编译器根据泛型自动推断类型 |
| 单参数可省略括号 | 若仅有一个参数 且省略了类型 ,则 () 可省略。例如:(e) → e(若保留类型如 (String e),则括号不可省) |
| 单行代码可省略大括号 | 若方法体仅有一行代码,{}、; 及 return 可同时省略 。例如:(x, y) -> { return x + y; } → (x, y) -> x + y |
4.2 省略演示
让我们继续使用排序的例子,看看代码是如何一步步"瘦身"的。
场景 :对整数数组进行降序排序。
4.2.1 第一阶段:Lambda完整格式
java
Arrays.sort(arr, (Integer o1, Integer o2) -> {
return o2 - o1;
});
4.2.2 第二阶段:省略参数类型
编译器可以通过arr数组的类型推导出o1和o2都是Integer类型。
java
Arrays.sort(arr, (o1, o2) -> {
return o2 - o1;
});
4.2.3 第三阶段:省略大括号、return和分号
因为方法体只有一行代码,我们可以将其简化为一行流式写法。
java
// 最终形态
Arrays.sort(arr, (o1, o2) -> o2 - o1);
5. 实战练习:字符串长度排序
为了巩固Lambda的使用,我们来完成一个实际的小练习。
需求:
- 定义一个字符串数组
{"a", "aaaa", "aaa", "aa"}。 - 使用
Arrays.sort进行排序。 - 排序规则 :按照字符串的长度进行降序排序(长的在前,短的在后)。
5.1 传统匿名内部类写法
java
package com.example;
import java.util.Arrays;
import java.util.Comparator;
public class ArraysDemo {
public static void main(String[] args) {
String[] arr = {"a", "aaaa", "aaa", "aa"};
Arrays.sort(arr, new Comparator<String>() {
@Override
public int compare(String o1, String o2) {
// 按照长度比较:o2长度 - o1长度
return o2.length() - o1.length();
}
});
//输出结果:[aaaa, aaa, aa, a]
System.out.println(Arrays.toString(arr));
}
}
5.2 Lambda省略写法(推荐)
我们可以直接利用省略规则,将代码简化到最极致:
java
package com.example;
import java.util.Arrays;
public class ArraysDemo {
public static void main(String[] args) {
String[] arr = {"a", "aaaa", "aaa", "aa"};
// 参数类型省略 + 大括号/return省略
Arrays.sort(arr,(o1,o2) -> o2.length()-o1.length());
//输出结果:[aaaa, aaa, aa, a]
System.out.println(Arrays.toString(arr));
}
}

5.3 扩展知识:空指针风险与防御式编程
在实际开发中,如果数组中混入了 null 值,直接调用 .length() 会立即抛出 NullPointerException。
Java 8 的 Comparator 提供了 nullsFirst 或 nullsLast 工厂方法,能让我们优雅地定义空值的排序位置(排最前或排最后),从而避免程序崩溃。
示例:将 null 值视为最小值,排在最前面
java
// 处理可能包含 null 的字符串数组
Arrays.sort(arr, Comparator.nullsFirst(Comparator.comparing(String::length)));