你如何理解java中Stream流的Function,Consumer,Predicate,Supplier,Optional这几个接口呢?

214 阅读3分钟

给我讲讲java中stream流的Function,Consumer,Predicate,Supplier,Optional,我不太理解它们的具体用法,最好能深入浅出,通俗易懂

一、‌核心功能接口‌
Java 的函数式接口(如 FunctionConsumer 等)
是为 ‌Lambda 表达式和 Stream 流‌设计的工具,它们像“零件”一样组合起来处理数据。

1.Function<T, R>:加工数据的工厂🏭‌
‌功能‌:接收一个输入值(T),处理后返回结果(R)。
‌场景‌:适合 ‌转换数据‌,比如将字符串转成数字,或对象转DTO。
‌代码示例‌:


// 示例1:将字符串转成其长度
Function<String, Integer> strToLength = s -> s.length();
System.out.println(strToLength.apply("Hello"));  // 输出:5

// 示例2:在Stream中用map转换数据
List<String> names = Arrays.asList("Alice", "Bob");
List<Integer> lengths = names.stream()
        .map(strToLength)  // 等价于 .map(s -> s.length())
        .collect(Collectors.toList());
// 结果:[5, 3]
2.Consumer<T>:只吃不吐的大胃王🍔‌
‌功能‌:接收一个输入值(T),但 ‌不返回任何结果‌。
‌场景‌:适合 ‌消费数据‌,比如打印日志、修改对象状态。
‌代码示例‌:


// 示例1:打印传入的值
Consumer<String> printer = s -> System.out.println("Value: " + s);
printer.accept("Test");  // 输出:Value: Test

// 示例2:在Stream中用forEach消费数据
List<String> names = Arrays.asList("Alice", "Bob");
names.stream()
     .forEach(printer);  // 输出每个名字
3.Predicate<T>:判断题考官✅❌‌
‌功能‌:接收一个输入值(T),返回 boolean 值。
‌场景‌:适合 ‌条件筛选‌,比如过滤集合中的元素。
‌代码示例‌:


// 示例1:判断字符串是否以"A"开头
Predicate<String> startsWithA = s -> s.startsWith("A");
System.out.println(startsWithA.test("Alice"));  // true

// 示例2:在Stream中用filter筛选数据
List<String> names = Arrays.asList("Alice", "Bob", "Anna");
List<String> aNames = names.stream()
        .filter(startsWithA)  // 等价于 .filter(s -> s.startsWith("A"))
        .collect(Collectors.toList());
// 结果:["Alice", "Anna"]
4.Supplier<T>:无中生有的魔法师✨‌
‌功能‌:不需要输入参数,直接返回一个值(T)。
‌场景‌:适合 ‌延迟生成数据‌,比如生成随机数、获取配置。
‌代码示例‌:


// 示例1:生成当前时间
Supplier<LocalDateTime> timeSupplier = () -> LocalDateTime.now();
System.out.println(timeSupplier.get());  // 输出当前时间

// 示例2:在Stream中生成无限序列
Stream.generate(() -> Math.random() * 100)  // 生成随机数流
      .limit(5)
      .forEach(System.out::println);  // 输出5个随机数
5.Optional<T>:防Null的保险箱🔒‌
‌功能‌:包装一个可能为 null 的值,强制开发者处理空值。
‌场景‌:避免 NullPointerException,明确处理可能为空的情况。
‌代码示例‌:


// 示例1:安全取值
String name = null;
Optional<String> optName = Optional.ofNullable(name);

// 正确做法:检查是否存在值
optName.ifPresent(s -> System.out.println(s));  // 不会执行(因为name是null)

// 示例2:设置默认值
String safeName = optName.orElse("Unknown");
System.out.println(safeName);  // 输出:Unknown

// 示例3:链式操作(避免多层null检查)
Optional<String> upperName = optName.map(String::toUpperCase);
System.out.println(upperName.orElse("N/A"));  // 输出:N/A
二、‌对比总结‌
‌接口‌	‌输入‌	‌输出‌	‌典型用途‌	‌类比‌
Function<T,R>	T	R	数据转换(如 map 操作)	工厂加工原材料 → 成品
Consumer<T>	T	void	消费数据(如 forEach 操作)	吃货吃掉食物
Predicate<T>	T	boolean	条件判断(如 filter 操作)	判断题对错
Supplier<T>	无	T	生成数据(如 generate 操作)	魔法师凭空变物
Optional<T>	-	-	安全处理 null	保险箱防丢
三、‌组合使用案例‌
假设有一个需求:‌从用户列表中找出年龄大于18岁的用户,提取他们的名字并转为大写,最后打印‌。

‌代码实现‌:

java
Copy Code
List<User> users = Arrays.asList(
    new User("Alice", 20),
    new User("Bob", 17),
    new User("Charlie", 22)
);

users.stream()
     .filter(user -> user.getAge() > 18)          // Predicate(过滤条件)
     .map(user -> user.getName().toUpperCase())   // Function(转换数据)
     .forEach(System.out::println);               // Consumer(消费数据)

// 输出:ALICE, CHARLIE
四、‌关键注意事项‌
‌避免滥用 Optional‌:

不要用 Optional 替代所有 null,主要针对返回值可能为空的场景。
避免直接调用 Optional.get()(可能抛异常),优先用 orElse() 或 ifPresent()。
‌函数式接口与 Lambda 简化‌:

尽量用 Lambda 表达式或方法引用简化代码(如 s -> s.length() → String::length)。
‌注意并行流中的线程安全‌:

SupplierConsumer 等接口的实现需确保线程安全。
通过理解这些接口的角色和作用,可以更灵活地编写函数式风格的 Java 代码,让 Stream 流操作更加简洁高效!