Optional 进阶:filter/ifPresent/博客项目重构(AI 辅助学习 Java 8)

27 阅读2分钟

上篇学了 Optional 基础用法。今天深入 filter、ifPresent、以及如何在博客项目中用 Optional 重构查询逻辑。


一、filter:条件过滤

filter 接收一个 Predicate,如果 Optional 有值且满足条件,返回包含该值的 Optional;否则返回空。

Optional<BlogPost> result = repository.findById(2L)
        .filter(BlogPost::isPublished);
// id=2 存在且已发布 -> Optional[BlogPost]
// id=2 存在但未发布 -> Optional.empty
// id=2 不存在       -> Optional.empty

链式 filter:多个条件依次过滤。

Optional<BlogPost> hot = repository.findById(2L)
        .filter(BlogPost::isPublished)
        .filter(b -> b.getViews() > 200);

二、ifPresent:有值时执行

repository.findById(1L).ifPresent(blog ->
    System.out.println("找到: " + blog.getTitle()));
// 找到就打印,没找到什么都不做

链式处理:先转换再消费。

repository.findById(2L)
        .map(BlogPost::getTitle)
        .ifPresent(title -> System.out.println("标题: " + title));

三、Optional 与 Stream 结合

在 Stream 中处理 Optional 时,需要过滤空值:

// Java 8 兼容写法
List<String> titles = Arrays.asList(1L, 2L, 3L).stream()
        .map(id -> repository.findById(id))
        .filter(Optional::isPresent)
        .map(Optional::get)
        .map(BlogPost::getTitle)
        .collect(Collectors.toList());

注意Optional.stream() 是 Java 9+ 才有的方法,Java 8 中需要用 filter + get 替代。

四、Repository 层用 Optional 重构

Spring Data JPA 的 JpaRepository.findById() 本身就返回 Optional<T>,这是推荐的做法。

传统写法

BlogPost blog = repository.findById(id);
if (blog == null) {
    throw new ResponseStatusException(HttpStatus.NOT_FOUND, "博客不存在");
}
return Result.success(blog);

Optional 写法

BlogPost blog = repository.findById(id)
        .orElseThrow(() -> new ResponseStatusException(HttpStatus.NOT_FOUND, "博客不存在"));
return Result.success(blog);

一行代码替代 if-null 抛异常。

存在就执行操作

// 存在就更新
repository.findById(id).ifPresent(existing -> {
    existing.setTitle(newTitle);
    repository.save(existing);
});

// 存在就删除
repository.findById(id).ifPresent(repository::delete);

// 不存在就创建
repository.findById(id).orElseGet(() -> repository.save(new BlogPost()));

五、orElse vs orElseGet 的陷阱

Optional<BlogPost> found = repository.findById(1L);

// orElse:即使找到了,new BlogPost() 也会执行
BlogPost blog = found.orElse(new BlogPost());

// orElseGet:只在没找到时才执行
BlogPost blog = found.orElseGet(() -> new BlogPost());

当默认值的创建有开销(如数据库查询、对象创建)时,优先用 orElseGet

六、运行结果

=== Optional filter ===
  id=2 且已发布: Stream 详解
  id=3 且已发布: 未找到
  id=2 已发布且>200阅读: Stream 详解

=== Optional ifPresent ===
  找到: Lambda 入门 (200 阅读)
  标题: Stream 详解

=== Optional + Stream ===
  查 id 1,2,3 的标题: [Lambda 入门, Stream 详解, Vue3 API]
  Java 8 兼容写法: [Lambda 入门, Stream 详解, Vue3 API]

=== Repository 层 Optional 实践 ===
  标题: Lambda 入门
  id=99: 博客不存在
  更新成功: 更新后的标题

七、Optional 最佳实践

场景推荐做法
返回值可能为空Optional<T> 包装
链式属性获取flatMap + map + orElse
为空时提供默认值orElse / orElseGet
为空时抛异常orElseThrow
为空时不执行ifPresent
条件过滤filter
不要做的事
get() 直接取值orElse / ifPresent 替代
用 Optional 做字段类型Optional 设计为返回值,不是字段类型
用 Optional 做方法参数用重载方法替代

八、下篇预告

周六我们将在博客项目中用 Optional 重构现有的 Controller 查询逻辑,告别所有的 if-null 判断。


系列文章导航


更多请关注我的微信公众号,感谢!

微信图片_20260427132414_18_737.jpg