从零读懂 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; }
}
相关推荐
代码小书生15 小时前
statistics,一个统计的 Python 库!
开发语言·python
jason.zeng@150220715 小时前
Androidr入门环境搭建
java·kotlin
摇滚侠15 小时前
整洁的桌面和任务栏 Java 开发工程师提效方法
java·开发语言
知识分享小能手15 小时前
R语言入门学习教程,从入门到精通,R语言数据计算与分组统计(9)
开发语言·学习·r语言
山居秋暝LS16 小时前
安装C++版opencv和opencv_contrib
开发语言·c++·opencv
每天都要加油呀!16 小时前
多租户中间件适配
java·多租户
老陈说编程16 小时前
12. LangChain 6大核心调用方法:invoke/stream/batch同步异步全解析,新手也能轻松学会
开发语言·人工智能·python·深度学习·机器学习·ai·langchain
014-code16 小时前
Java 并发中的原子类
java·开发语言·并发
alphageek816 小时前
Matlab linspace函数完全指南:从基础用法到进阶技巧
开发语言·其他·matlab
AI人工智能+电脑小能手16 小时前
【大白话说Java面试题】【Java基础篇】第29题:静态代理和动态代理的区别是什么
java·开发语言·后端·面试·代理模式