Java 函数式编程:核心思想与实践
函数式编程(Functional Programming)是一种以函数为核心的编程范式,强调不可变数据和无副作用,从根本上解决了并发编程中的数据竞争问题。Java 8 引入 Lambda 表达式和函数式接口,正式支持函数式编程风格,大幅简化了代码并提升了可读性。本文将从核心思想出发,详解 Lambda 表达式、函数式接口、方法引用等关键特性。
函数式编程的核心思想
函数式编程的两大支柱:
- 不可变数据(Immutability)
数据一旦创建就不可修改,任何操作都只会生成新数据,不会改变原始值。这避免了多线程环境下的数据竞争(多个线程同时修改同一数据)。
- 无副作用(No Side Effects)
函数的执行不会影响外部状态(如全局变量、输入参数),相同输入始终产生相同输出。这种 “纯函数” 特性让代码更易测试和推理。
函数式接口:Lambda 表达式的载体
函数式接口是函数式编程的基础,指仅包含一个抽象方法的接口(可包含默认方法或静态方法)。Java 8 提供 @FunctionalInterface 注解用于标记此类接口,编译器会检查其是否符合规范。
1 2 3 4 5 6 7 8 9 10 11 12
| @FunctionalInterface public interface Calculator { int compute(int a, int b); default void printResult(int result) { System.out.println("结果:" + result); } static void log() { System.out.println("计算完成"); } }
|
关键:Lambda 表达式本质是函数式接口的匿名实现,简化了接口实现的代码。
Lambda 表达式:简洁的函数实现
Lambda 表达式是函数式接口的简写形式,语法为:
语法规则
参数列表:
- 单个参数可省略括号:
a -> { ... }
- 无参数需用空括号:
() -> { ... }
- 多个参数用逗号分隔:
(a, b) -> { ... }
函数体:
与传统实现的对比
Lambda 表达式简化了接口实现的写法,对比传统方式:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23
| @FunctionalInterface interface Greeting { String sayHello(String name); }
public class LambdaDemo { public static void main(String[] args) { Greeting anonymous = new Greeting() { @Override public String sayHello(String name) { return "Hello, " + name; } }; Greeting lambda = name -> "Hello, " + name; System.out.println(anonymous.sayHello("Alice")); System.out.println(lambda.sayHello("Bob")); } }
|
可见,Lambda 表达式消除了匿名内部类的模板代码,直接聚焦核心逻辑。
Java 内置四大函数式接口
Java 8 在 java.util.function 包中提供了常用函数式接口,覆盖大多数场景,避免重复定义。
Consumer<T>:消费型接口
功能:接收类型 T 的参数,执行操作(无返回值)。
抽象方法:void accept(T t)
1 2 3 4 5 6 7 8 9 10 11 12 13 14
| import java.util.function.Consumer;
public class ConsumerDemo { public static void main(String[] args) { Consumer<String> printer = s -> System.out.println("消费:" + s); printer.accept("测试数据"); Consumer<String> upperCase = s -> System.out.println(s.toUpperCase()); printer.andThen(upperCase).accept("hello"); } }
|
Supplier<T>:供给型接口
功能:无参数,返回类型 T 的结果(提供数据)。
抽象方法:T get()
1 2 3 4 5 6 7 8 9 10 11 12 13
| import java.util.function.Supplier;
public class SupplierDemo { public static void main(String[] args) { Supplier<Integer> randomInt = () -> (int) (Math.random() * 100); System.out.println("随机数:" + randomInt.get()); Supplier<String> defaultName = () -> "默认名称"; System.out.println(defaultName.get()); } }
|
Function<T, R>:函数型接口
功能:接收类型 T 的参数,返回类型 R 的结果(数据转换)。
抽象方法:R apply(T t)
1 2 3 4 5 6 7 8 9 10 11 12 13 14
| import java.util.function.Function;
public class FunctionDemo { public static void main(String[] args) { Function<String, Integer> strLength = s -> s.length(); System.out.println(strLength.apply("hello")); Function<Integer, Integer> doubleIt = n -> n * 2; int result = strLength.andThen(doubleIt).apply("test"); } }
|
Predicate<T>:断言型接口
功能:接收类型 T 的参数,返回布尔值(条件判断)。
抽象方法:boolean test(T t)
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
| import java.util.function.Predicate;
public class PredicateDemo { public static void main(String[] args) { Predicate<Integer> isEven = n -> n % 2 == 0; System.out.println(isEven.test(4)); Predicate<Integer> isGreaterThan5 = n -> n > 5; boolean result = isEven.and(isGreaterThan5).test(6); System.out.println(result); } }
|
Lambda 递归:特殊用法
Lambda 表达式支持递归,但需注意:递归调用的函数必须是实例变量或静态变量(避免初始化时的循环依赖)。
示例:用 Lambda 实现阶乘计算
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16
| @FunctionalInterface interface IntCalculator { int calculate(int n); }
public class LambdaRecursion { public static void main(String[] args) { IntCalculator factorial; factorial = n -> n == 0 ? 1 : n * factorial.calculate(n - 1); System.out.println("5! = " + factorial.calculate(5)); } }
|
原理:factorial 变量先声明后赋值,Lambda 表达式中引用自身时,变量已完成声明(但未初始化),避免了编译错误。
方法引用:Lambda 的简化形式
当 Lambda 表达式的逻辑已通过现有方法实现时,可通过方法引用进一步简化代码。语法为 类名/对象名::方法名,无需参数列表。
三种方法引用形式
(1)对象::实例方法
引用现有对象的实例方法。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20
| public class MethodRefDemo { public static void main(String[] args) { StringFormatter formatter = new StringFormatter(); Function<String, String> lambda = s -> formatter.format(s); Function<String, String> methodRef = formatter::format; System.out.println(methodRef.apply("test")); } }
class StringFormatter { public String format(String s) { return "FORMATTED: " + s; } }
|
(2)类::静态方法
引用类的静态方法。
1 2 3 4 5 6 7 8 9 10 11 12 13
| import java.util.function.Function;
public class StaticMethodRef { public static void main(String[] args) { Function<Integer, Integer> lambda = n -> Math.abs(n); Function<Integer, Integer> methodRef = Math::abs; System.out.println(methodRef.apply(-100)); } }
|
(3)类::实例方法
引用类的实例方法(特殊场景:Lambda 第一个参数是方法的调用者,第二个参数是方法的参数)。
1 2 3 4 5 6 7 8 9 10 11 12 13 14
| import java.util.function.BiPredicate;
public class ClassInstanceMethodRef { public static void main(String[] args) { BiPredicate<String, String> lambda = (s1, s2) -> s1.equals(s2); BiPredicate<String, String> methodRef = String::equals; System.out.println(methodRef.test("a", "a")); } }
|
构造器引用
引用类的构造器,语法为 类名::new,等价于创建对象的 Lambda 表达式。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28
| import java.util.function.Supplier; import java.util.function.Function;
public class ConstructorRef { static class User { private String name; public User() { this.name = "默认用户"; } public User(String name) { this.name = name; } public String getName() { return name; } } public static void main(String[] args) { Supplier<User> noArgCtor = User::new; System.out.println(noArgCtor.get().getName()); Function<String, User> argCtor = User::new; System.out.println(argCtor.apply("Alice").getName()); } }
|
函数式编程的优势与适用场景
优势:
- 代码简洁:Lambda 和方法引用消除模板代码,逻辑更清晰。
- 易于并发:不可变数据和无副作用避免了线程安全问题。
- 函数组合:通过
andThen、compose 等方法组合多个函数,实现复杂逻辑。
- 适配 Stream API:Stream 的中间操作(如
filter、map)依赖函数式接口,实现声明式数据处理。
适用场景:
- 集合数据处理(结合 Stream API)。
- 事件处理(如按钮点击回调)。
- 并发编程(避免共享状态修改)。
- 函数式接口的快速实现(如自定义排序、条件判断)。