Lambda表达式与匿名内部类的深度理解
一、Lambda 是什么
Lambda就是:简化写法的匿名内部类
作用:不用写多余类名、方法名,只保留「参数 + 逻辑」
专门用来简化函数式接口的代码。
二、核心语法公式:
(参数列表) -> { 方法体逻辑 }
拆解:
- ():放方法参数,无参就空括号
- ->:Lambda 箭头,固定写法
- {}:业务代码逻辑
三、前置关键:函数式接口
只要接口里只有一个抽象方法,就是函数式接口
可以用 @FunctionalInterface 标注。
示例:
@FunctionalInterface
interface MyFunc {
void sayHello(); //只有方法声明,没有{}方法体
}
interface是定义接口的关节字,类比Java中的类class
接口是一套「行为规范、规则清单」,只定义方法声明,不写具体实现。
谁实现这个接口,必须重写这个方法,自己写具体逻辑。
Lambda 表达式 只能用在接口上,而且这个接口只能有一个抽象方法,叫函数式接口。
四、从传统写法 → Lambda 对比
1. 传统匿名内部类写法
MyFunc f = new MyFunc() {
@Override
public void sayHello() {
System.out.println("Hello 传统写法");
}
};
f.sayHello();
匿名内部类:(需要用到接口中的方法并且只需要使用一次时,专门定义一个类去实现接口过于麻烦,这个时候就用匿名内部类),直接:
接口名 变量 = new 接口名{重写接口中的方法}
这样就不用单独写一个 class 类名 implements 接口,
原地直接 new 接口,当场重写接口里的抽象方法,(接口里的抽象方法必须全部重写)
适合只用一次、不想单独建一个类的场景。
省去定义实现类,原地创建、原地重写。
不是只需要重写一个方法,而是接口里的方法都要重写
- 接口有2 个、3 个抽象方法,匿名内部类也能用
- 只是如果只有 1 个抽象方法(函数式接口),后面可以进一步改成 Lambda
Lambda 简化后
MyFunc f = () -> System.out.println("Hello Lambda");
f.sayHello();
五、带参数、返回值的Lambda写法
1. 一个参数
@FunctionalInterface
interface Calc {
int add(int a);
}
// Lambda
Calc c = a -> a * 2;
System.out.println(c.add(5)); // 10
2. 多个参数+方法体多行
@FunctionalInterface
interface Calc2 {
int add(int a, int b);
}
Calc2 c2 = (a,b) -> {
int sum = a + b;
return sum;
};
System.out.println(c2.add(3,4)); // 7
六、Lambda省略规则
1. 参数只有一个:可以省略 ()
a -> System.out.println(a)
2. 方法体只有一行:可以省略{}和return
(a,b) -> a + b
3. 参数类型:可全部省略,编译器自动推断
4. 无参数:必须保留空括号 ()
七、开发最常用场景:集合遍历
普通 for 循环
List<String> list = Arrays.asList("张三","李四");
for (String s : list) {
System.out.println(s);
}
Lambda 简化遍历
list.forEach(s -> System.out.println(s));
八、你要记住的核心结论
- Lambda 只能用在函数式接口上(接口只有一个抽象方法)
- 本质:简化匿名内部类,去掉冗余模板代码
- 格式:(参数) -> 逻辑
- 日常最多用:集合遍历、Stream 流、线程启动、Comparator 排序
接口和抽象类的区别
1. 关键字
- 抽象类:abstract class
- 接口:interface
2. 构造方法 - 抽象类:有构造方法(可以被子类调用)
- 接口:没有构造方法
3. 成员变量 - 抽象类:可以有普通变量、常量
- 接口:只能是常量(默认 public static final)
4. 普通方法 - 抽象类:可以有普通有体方法 + 抽象无体方法
- 接口:JDK8 之前只能抽象方法;JDK8 后可以有默认方法、静态方法
5. 继承 / 实现关系 - 抽象类:单继承 extends 只能继承一个
- 接口:多实现 implements 可以同时实现多个接口
6. 设计思想 - 抽象类:is-a 是什么(学生是人、动物是生物)
- 接口:has-a / 具备某种能力(会跑、会飞、会排序)
- 普通接口多个抽象方法 → 匿名内部类 必须全重写(Java 接口里的抽象方法,默认自带 public abstract,你不写,编译器也会自动帮你补上。)
- 不想重写没用的方法 → 要么改成函数式接口,要么给其他方法加 default 默认实现
- Lambda 只能用在只有一个抽象方法的接口,就是为了避开 “要重写一堆没用方法” 的麻烦。
代码解释
List<String> list = List.of("张三","李四","王五");
// 挨个取出每个元素 s,打印
list.forEach(s -> System.out.println(s));
forEach = for each
翻译:对每一个、遍历每一个
就是:把集合里每一个元素,挨个拿出来执行一遍操作。
forEach 是 Java 集合 List/Set 自带的遍历方法
底层就是帮你封装好了循环,不用你手写 for 循环。
forEach 就是简化版增强 for 循环。
结合刚学的 Lambda / 匿名内部类
forEach 方法接收一个函数式接口 Consumer
所以可以传:
- 匿名内部类
- Lambda 表达式
老式匿名内部类写法
list.forEach(new Consumer<String>() {
@Override
public void accept(String s) {
System.out.println(s);
}
});
简化成 Lambda:
list.forEach(s -> System.out.println(s));
浙公网安备 33010602011771号