1. 引言

在Java中,我们可以利用函数式编程特性将方法作为参数传递给其他方法。这主要通过lambda表达式方法引用函数式接口实现。本文将探讨几种常见实现方式,帮你避开常见坑点。

2. 使用接口和匿名内部类

Java 8之前主要靠接口和匿名内部类实现方法参数化。看个经典案例:

interface Operation {
    int execute(int a, int b);
}

我们定义了一个Operation接口,包含抽象方法execute()任何实现该接口的类都必须提供execute()的具体实现

接着创建接收Operation实例的方法:

int performOperation(int a, int b, Operation operation) {
    return operation.execute(a, b);
}

内部调用operation.execute(a, b),执行传入的Operation实例方法。调用时这样写:

int actualResult = performOperation(5, 3, new Operation() {
    @Override
    public int execute(int a, int b) {
        return a + b;
    }
});

这里使用匿名内部类创建Operation实例。这个类没有名字,但动态实现了execute()方法。在匿名类中重写了execute(),简单返回两数之和。

最后验证结果:

assertEquals(8, actualResult);

⚠️ 这种写法在Java 8前很常见,但代码冗长,现在基本被lambda替代。

3. 使用Lambda表达式

Java 8引入lambda表达式,让方法参数传递更简洁优雅:

@FunctionalInterface
interface Operation {
    int execute(int a, int b);
}

@FunctionalInterface标注接口,确保它只有一个抽象方法。调用时:

int actualResult = performOperation(5, 3, (a, b) -> a + b);

第三个参数直接用lambda表达式(a, b) -> a + b替代匿名内部类。验证结果相同:

assertEquals(8, actualResult);

✅ lambda表达式比匿名内部类更简洁,可读性更强,是现代Java首选方案。

4. 使用方法引用

方法引用是lambda的简化版,专门用于调用特定方法的场景。看如何实现相同功能:

先定义一个普通方法:

int add(int a, int b) {
    return a + b;
}

然后通过对象::方法名类名::方法名语法传递方法引用

int actualResult = performOperation(5, 3, FunctionParameter::add);
assertEquals(8, actualResult);

这里FunctionParameter::add引用了FunctionParameter类的add()方法。**相当于把add()的行为作为参数传递给performOperation()**。

performOperation()内部,这个方法引用被当作Operation函数式接口的实例处理。

5. 使用Function类

Java 8的java.util.function包提供了常用函数式接口。其中**BiFunction**表示接收两个参数并返回结果的函数:

先创建接收BiFunction的方法:

int executeFunction(BiFunction<Integer, Integer, Integer> function, int a, int b) {
    return function.apply(a, b);
}

apply()方法用于执行函数。调用时用lambda创建BiFunction实例:

int actualResult = executeFunction((a, b) -> a + b, 5, 3);

lambda表达式(a, b) -> a + b表示加法运算,53作为输入参数。验证结果:

assertEquals(8, actualResult);

BiFunction适合需要处理两个参数的通用场景,比自定义接口更省事。

6. 使用Callable类

Callable接口来自java.util.concurrent包,表示可返回结果并可能抛出异常的任务,特别适合并发场景:

先创建接收Callable的方法:

int executeCallable(Callable<Integer> task) throws Exception {
    return task.call();
}

call()方法执行任务并返回结果,可能抛出异常。用lambda定义任务:

Callable<Integer> task = () -> 5 + 3;

这个lambda计算5+3。然后调用方法:

int actualResult = executeCallable(task);
assertEquals(8, actualResult);

⚠️ 虽然Callable能实现方法参数传递,但主要用于并发编程,简单场景没必要用这么重的方案。

7. 总结

本文介绍了Java中传递方法参数的四种方式:

方式 适用场景 优点 缺点
匿名内部类 遗留系统维护 兼容旧版本 代码冗长
Lambda表达式 简单操作 简洁高效 复杂逻辑可读性差
方法引用 现有方法复用 极致简洁 仅限特定方法
Function类 通用操作 无需自定义接口 泛型可能增加复杂度
Callable类 并发任务 支持异常处理 线程开销大

简单粗暴的选择建议:

  • ✅ 日常开发优先用lambda表达式
  • ✅ 复用已有方法时用方法引用
  • ❌ 避免在新代码中使用匿名内部类
  • ⚠️ 并发场景才考虑Callable

示例代码已上传至GitHub,欢迎踩坑交流。


原始标题:How to Pass Method as Parameter in Java | Baeldung