JAVA后端开发——forEach 与方法引用(::)详解
1、案例代码
@Overridepublic List<ModelDetailVo> selectModelList(ModelQuery query){List<ModelDetailVo> modelList = modelMapper.selectModelList(query);modelList.forEach(this::enrichModelDetailVo);return modelList;}private ModelDetailVo enrichModelDetailVo(ModelDetailVo vo) {if (vo == null) {return null;}if (vo.getSizeInBytes() != null) {vo.setSizeFormatted(formatBytes(vo.getSizeInBytes()));}if (vo.getModelTypeId() != null) {String typeName = DictUtils.getDictLabel("cloud_model_type", String.valueOf(vo.getModelTypeId()));vo.setModelTypeName(typeName);}return vo;}
这段代码的核心功能是:遍历 modelList 集合中的每一个 ModelDetailVo 对象,并对每个对象执行 enrichModelDetailVo 这个方法。这种写法是传统 for 循环的现代化替代方案,它利用了 Java 8 的函数式编程特性,使代码更简洁、更具表达力。
这个结构可以拆解为两个核心组件来理解:
forEach() 方法。
方法引用 (::) 操作符。
2. 核心知识点一:List.forEach() 方法
forEach() 方法是在 Java 8 中被添加到 java.lang.Iterable 接口的,这意味着所有的集合(List、Set 等)都拥有这个方法。
作用: 为集合中的每个元素执行一次给定的操作。
方法签名: void forEach(Consumer<? super T> action)
参数: 它接受一个 Consumer (消费者) 类型的参数。Consumer 是一个函数式接口,它代表了一个接受单个输入参数并且没有返回值的操作。通俗地讲,它就是“消费”一个元素,然后用这个元素去做一些事情。
3. 核心知识点二:方法引用 (::) 操作符
双冒号 :: 是 Java 8 中引入的方法引用操作符。它是一种用于直接引用已有方法的 Lambda 表达式的简化写法。当 Lambda 表达式的主体只是调用一个已存在的方法时,方法引用可以让代码更加清晰和易读。
让我们来分析 this::enrichModelDetailVo 这个部分:
this: 指代当前类的实例对象。
::: 分隔符,用于连接对象/类与方法名。
enrichModelDetailVo: 需要被引用的实例方法名。
工作原理:
方法引用 this::enrichModelDetailVo 会被 Java 编译器自动转换成一个实现了 Consumer 接口的实例,而这正好是 forEach 方法所需要的参数类型。
4. 融会贯通:三种等价的代码写法
modelList.forEach(this::enrichModelDetailVo);
代码解读: “对于 modelList 中的每一个元素,都调用当前对象的 enrichModelDetailVo 方法,并将该元素作为参数传进去。”
方法引用本质上是 Lambda 表达式的一种语法。下面的代码在功能上与写法一完全相同。
modelList.forEach(vo -> this.enrichModelDetailVo(vo));
代码解读: “对于 modelList 中的每一个元素(我们临时称之为 vo),执行以下操作:调用 this.enrichModelDetailVo() 方法,并把 vo 作为参数传入。”
for (ModelDetailVo vo : modelList) {this.enrichModelDetailVo(vo);
}
代码解读: “创建一个循环来遍历 modelList。在每一次循环中,将当前元素赋值给变量 vo,然后用 vo 作为参数调用 this.enrichModelDetailVo() 方法。”
5. 使用 forEach 与方法引用的优势
对比维度 | forEach 与方法引用 | 传统 for 循环 |
编程风格 | 声明式 (Declarative) - “做什么” | 命令式 (Imperative) - “怎么做” |
代码可读性 | 高。意图非常清晰:将一个操作应用于所有元素。 | 中。循环的样板代码可能会掩盖其核心操作。 |
代码简洁性 | 非常高。一行富有表现力的代码即可完成。 | 较低。需要多行代码和循环语法(for, (), {})。 |
代码表达力 | 高。清晰地将数据(modelList)和操作(enrichModelDetailVo)分离开。 | 较低。迭代逻辑和业务操作逻辑耦合在一起。 |
6. 总结
它是什么: modelList.forEach(this::enrichModelDetailVo); 是一种简洁的函数式循环,它将当前对象的 enrichModelDetailVo 方法应用到了 modelList 的每一个元素上。
何时使用: 当你需要对一个集合的每个元素调用一个已经存在的、单一的方法时,这是最理想的选择。被引用的方法签名必须与所需的函数式接口兼容(forEach 需要的是 Consumer,即接受一个参数,返回 void)。这里的例子中 enrichModelDetailVo 方法虽然有返回值,但 forEach 会忽略它,因此是兼容的。
何时避免: 如果你的循环体逻辑比单个方法调用更复杂(例如,包含多个 if/else 分支、调用多个方法或需要处理异常),那么使用多行 Lambda 表达式 forEach(vo -> { ... }); 或者传统的 for 循环可能会让代码的逻辑更清晰。