从匿名内部类到函数式编程的演进
本页系统讲解 Lambda 语法、函数式接口、方法引用、实际项目中的使用技巧。
定义:Lambda 是一种更简洁的写法,用来表示只包含一个抽象方法的接口实例(函数式接口)。
解决的问题:
// 传统写法:匿名内部类
new Thread(new Runnable() {
@Override
public void run() {
System.out.println("Hello");
}
}).start();
// Lambda 写法
new Thread(() -> System.out.println("Hello")).start();
// 形式: (参数列表) -> { 方法体 }
// 例: (a, b) -> a + b// 1. 无参数,无返回值
Runnable r1 = () -> System.out.println("hello");
// 2. 有一个参数,无返回值
Consumer c1 = (s) -> System.out.println(s);
Consumer c2 = s -> System.out.println(s); // 参数只有一个时可以省略括号
// 3. 有多个参数,有返回值
BinaryOperator add1 = (a, b) -> { return a + b; };
BinaryOperator add2 = (a, b) -> a + b; // 方法体只有一行时可以省略 return 和大括号
// 4. 指定参数类型
BinaryOperator add3 = (Integer a, Integer b) -> a + b; // 一般省略
// 5. 复杂 Lambda
Function length = s -> {
System.out.println("calc length");
return s.length();
}; 函数式接口:只包含一个抽象方法的接口,可以有默认方法和静态方法。
@FunctionalInterface
public interface MyFunc {
void run(); // 只能有一个抽象方法
default void log() {
System.out.println("default");
}
static void util() {
System.out.println("static");
}
}JDK 内置常见函数式接口(java.util.function):
@FunctionalInterface
interface Calculator {
int calc(int a, int b);
}
public class LambdaDemo {
public static void main(String[] args) {
// 1. 使用 Lambda 实现
Calculator add = (a, b) -> a + b;
Calculator multiply = (a, b) -> a * b;
// 2. 作为参数传递
int result = operate(10, 20, add);
System.out.println(result); // 30
}
public static int operate(int a, int b, Calculator c) {
return c.calc(a, b);
}
}注意:接口上加 @FunctionalInterface 不是必须的,但可以在编译期帮你检查是否只有一个抽象方法。
方法引用:是对 Lambda 的进一步简化,用 :: 引用已有方法。
四种形式:
List list = Arrays.asList("b", "a", "c");
// 1. 静态方法引用:ClassName::staticMethod
Collections.sort(list, String::compareToIgnoreCase);
// 2. 特定对象的实例方法引用:instance::method
PrintStream out = System.out;
list.forEach(out::println);
// 3. 特定类型任意对象的实例方法引用:ClassName::method
Function func = String::length; // 等价于 s -> s.length()
// 4. 构造器引用:ClassName::new
Supplier> listSupplier = ArrayList::new;
List newList = listSupplier.get();
核心规则:方法引用的参数列表和返回值必须与目标函数式接口的抽象方法兼容。
// 函数式接口
@FunctionalInterface
interface Converter {
T convert(F from);
}
// 目标方法
public class NumberUtils {
public static Integer parse(String s) {
return Integer.parseInt(s);
}
}
// 方法引用
Converter c1 = NumberUtils::parse; // 等价于 s -> NumberUtils.parse(s)
// 如果方法签名不匹配,会编译错误 规则:Lambda 可以访问有效 final 的外部局部变量。
public void test() {
int x = 10; // 有效 final:后面没有修改
Runnable r = () -> {
System.out.println(x); // 允许
};
// x = 20; // 编译错误:x 被 Lambda 捕获,必须是 final 或有效 final
}
// 原因:
// JVM 会将 x 拷贝一份到 Lambda 对象中,修改外部变量会导致不一致public class Demo {
public void test() {
Runnable r1 = new Runnable() {
@Override
public void run() {
System.out.println(this.getClass().getName());
}
};
Runnable r2 = () -> {
System.out.println(this.getClass().getName());
};
}
}
// 输出分析:
// 匿名内部类中的 this:指向匿名内部类实例
// Lambda 中的 this:指向外部类 Demo 的实例区别总结:
@FunctionalInterface
interface SerializableFunc extends java.io.Serializable {
void run();
}
SerializableFunc f = () -> System.out.println("ok");
// 现在 f 可以被序列化// 不推荐:业务逻辑太复杂
orderList.stream()
.filter(o -> {
// 很多 if-else,调用多个 service
return complexCheck(o);
})
.collect(Collectors.toList());
// 推荐:提取为命名方法
orderList.stream()
.filter(this::validOrder)
.collect(Collectors.toList());