从零读懂 Java 函数式接口:Function、Consumer、Supplier、Predicate

Java 8 在 java.util.function 包中定义了四大核心函数式接口,分别是 Function(转换)、Consumer(消费)、Supplier(供给)和 Predicate(判断)。它们构成了 Stream 流式编程和 Lambda 表达式的基础。

文章目录

一、核心四大接口对比

接口 人称 输入 输出 核心方法 主要用途 通俗理解
Function<T,R> 函数型 T R R apply(T t) 类型转换、映射 变形金刚:把苹果变成苹果汁
Consumer 消费型 T void void accept(T t) 遍历、打印、修改属性 黑洞:吃进苹果,不产出
Supplier 供给型 T T get() 工厂、数据生成 下蛋母鸡:只管生苹果
Predicate 断言型 T boolean boolean test(T t) 条件过滤、判断 质检员:判断是不是苹果

二、Stream方法对应关系速查

Stream 方法 需要的接口 作用 示例
filter() Predicate 条件过滤 .filter(s -> s.getScore() >= 60)
map() Function<T,R> 类型转换 .map(s -> s.getName())
forEach() Consumer 遍历操作 .forEach(System.out::println)
collect() Supplier + BiConsumer 收集结果 .collect(Collectors.toList())
anyMatch() Predicate 是否存在匹配 .anyMatch(s -> s.getScore() == 100)
sorted() Comparator 排序 .sorted((a,b) -> a.getScore() - b.getScore())

三、场景

  • 处理学生列表
java 复制代码
import java.util.*;
import java.util.function.*;
import java.util.stream.Collectors;

public class StreamFunctionExample {
    public static void main(String[] args) {
        // 准备数据
        List<Student> students = Arrays.asList(
            new Student("张三", 85, "计算机"),
            new Student("李四", 58, "数学"),
            new Student("王五", 92, "计算机"),
            new Student("赵六", 45, "物理"),
            new Student("小明", 78, "数学")
        );
        
        // 1. Predicate 使用:过滤及格的学生(成绩 >= 60)
        Predicate<Student> isPass = student -> student.getScore() >= 60;
        
        // 2. Function 使用:将 Student 转换为 String(姓名:成绩)
        Function<Student, String> toNameScore = student -> 
            student.getName() + ":" + student.getScore();
        
        // 3. Consumer 使用:打印信息
        Consumer<String> printInfo = info -> System.out.println("及格学生: " + info);
        
        // 4. Supplier 使用:生成默认学生对象(供给型)
        Supplier<Student> defaultStudent = () -> new Student("默认学生", 0, "未知");
        
        // ============ Stream 链式调用 ============
        System.out.println("=== 及格学生列表 ===");
        students.stream()
            .filter(isPass)                    // Predicate: 过滤
            .map(toNameScore)                  // Function: 转换
            .forEach(printInfo);               // Consumer: 消费打印
        
        // ============ Predicate 组合使用 ============
        System.out.println("\n=== 组合条件过滤 ===");
        Predicate<Student> isComputer = student -> "计算机".equals(student.getMajor());
        Predicate<Student> isHighScore = student -> student.getScore() >= 90;
        
        students.stream()
            .filter(isComputer.and(isHighScore))  // 计算机专业且高分(and组合)
            // .filter(isComputer.or(isHighScore)) // 或者用 or 组合
            .forEach(s -> System.out.println(s.getName() + " - " + s.getScore() + "分"));
        // 输出: 王五 - 92分
        
        // ============ Function 组合使用 ============
        System.out.println("\n=== 成绩等级转换 ===");
        Function<Integer, String> scoreToGrade = score -> {
            if (score >= 90) return "A";
            if (score >= 75) return "B";
            if (score >= 60) return "C";
            return "D";
        };
        
        // 组合: Student -> 成绩 -> 等级
        Function<Student, String> studentToGrade = 
            student -> scoreToGrade.apply(student.getScore());
        
        students.stream()
            .filter(isPass)
            .map(studentToGrade)
            .forEach(grade -> System.out.print(grade + " "));  // 输出: C B A B
        
        // ============ Consumer 使用:不同消费方式 ============
        System.out.println("\n\n=== 多种消费方式 ===");
        Consumer<Student> printDetail = s -> 
            System.out.printf("姓名:%-4s 成绩:%3d 专业:%s%n", 
                s.getName(), s.getScore(), s.getMajor());
        
        Consumer<Student> collectFail = s -> 
            System.out.println("需要补考: " + s.getName());
        
        students.stream()
            .filter(s -> s.getScore() < 60)
            .forEach(collectFail);  // 输出: 需要补考: 李四, 需要补考: 赵六
        
        // ============ Supplier 使用:orElseGet 提供默认值 ============
        System.out.println("\n=== Supplier 提供默认值 ===");
        // 查找数学专业最高分,如果不存在则使用 Supplier 提供默认值
        Student topMathStudent = students.stream()
            .filter(s -> "数学".equals(s.getMajor()))
            .max(Comparator.comparingInt(Student::getScore))
            .orElseGet(defaultStudent);  // Supplier 在这里被调用
        
        System.out.println("数学专业最高分: " + topMathStudent.getName() 
            + " (" + topMathStudent.getScore() + "分)");
        // 输出: 数学专业最高分: 李四 (58分)  注意:如果有多个数学专业,会取最高分
        
        // 查找不存在专业的学生
        Student topArtStudent = students.stream()
            .filter(s -> "美术".equals(s.getMajor()))
            .max(Comparator.comparingInt(Student::getScore))
            .orElseGet(defaultStudent);  // Supplier 提供默认值
        System.out.println("美术专业最高分: " + topArtStudent.getName());
        // 输出: 美术专业最高分: 默认学生
        
        // ============ 综合实战:统计及格学生平均分 ============
        System.out.println("\n=== 综合统计 ===");
        double avgScore = students.stream()
            .filter(isPass)                      // Predicate: 只统计及格的
            .mapToInt(Student::getScore)         // Function 特化版: 转换为 int
            .average()                           // 计算平均值
            .orElse(0.0);                        // Supplier: 没有数据时返回0
        
        System.out.printf("及格学生平均分: %.2f%n", avgScore);  // 输出: 85.00
        // (85 + 92 + 78) / 3 = 85
    }
}

// 学生实体类
class Student {
    private String name;
    private int score;
    private String major;
    
    public Student(String name, int score, String major) {
        this.name = name;
        this.score = score;
        this.major = major;
    }
    
    public String getName() { return name; }
    public int getScore() { return score; }
    public String getMajor() { return major; }
}
相关推荐
533_3 小时前
[vxe-table] 表头:点击出现输入框
android·java·javascript
天若有情6733 小时前
颠覆C++传统玩法!Property属性与伪类,开辟静态语言新维度
java·c++·servlet
写不来代码的草莓熊3 小时前
el-date-picker ,自定义输入数字自动转换显示yyyy-mm-dd HH:mm:ss格式 【仅双日历 datetimerange专用】
开发语言·前端·javascript
硅基导游3 小时前
linux系统与进程内存使用情况探测
java·linux·运维
I疯子3 小时前
2026-04-13 打卡第 6 天
开发语言·python
LucaJu3 小时前
Java + EasyExcel 实现单个接口导出多个Excel
java·excel
断眉的派大星3 小时前
值传递和引用传递
开发语言
大邳草民3 小时前
Python 对象模型与属性访问机制
开发语言·笔记·python
xyq20243 小时前
Swift 下标脚本
开发语言