【发布时间】:2018-12-02 13:40:14
【问题描述】:
我很想知道为什么 Java 的 Optional 没有提供类似于 Stream's one 的 peek 方法。
Stream 接口的peek 方法javadoc 状态:
- @apiNote 此方法的存在主要是为了支持调试,您希望在元素流过管道中的某个点时查看它们
这几乎完全描述了我的用例:
@Override
@Transactional
public User getUserById(long id) {
return repository.findById(id)
.peek(u -> logger.debug("Found user = {} by id = {}", u, id))
.orElseThrow(() -> new UserNotFoundException("id = " + id));
}
(repository.findById 返回Optional<User>(见CrudRepository#findById))
但它不会编译,因为Optional 上没有peek 方法。
因此,如果没有 peek 方法,上述所有内容都将转换为:
@Override
@Transactional
public User getUserById(long id) {
Optional<User> userOptional = repository.findById(id);
if (userOptional.isPresent()) {
logger.debug("Found user = {} with id = {}", userOptional.get(), id);
}
return userOptional.orElseThrow(() -> new UserNotFoundException("id = " + id));
}
也可以这样做(见answer):
@NoArgsConstructor(access = PRIVATE)
public abstract class OptionalUtils {
public static <T> UnaryOperator<T> peek(Consumer<T> consumer) {
return t -> {
consumer.accept(t);
return t;
};
}
}
并将其与map 方法一起使用:
return repository.findById(id)
.map(OptionalUtils.peek(u -> logger.debug("Found user = {} with id = {}", u, id)))
.orElseThrow(() -> new UserNotFoundException("id = " + id));
但我认为这是对 Optional 的一种黑客攻击,而不是干净的用法。
从 Java 9 开始,可以将 Optional 转换为 Stream,但流没有 orElseThrow 方法(显然不应该)。
也可以使用ifPresent 执行相同的操作,但它会返回void。 (对我来说,ifPresent 似乎不应该返回 void 以外的任何东西)
我是不是在滥用Optional?
peek 方法的缺失是故意的吗? (但同时 Vavr 的 Option 确实提供了 peek 方法。)
或者只是被认为不值得?
【问题讨论】:
-
可能是因为它的设计者认为不值得,拥有
ifPresent()就足够了。 -
如果你使用
User user = repository.findById().orElseThrow(...); logger.debug("...", user, id); return user而不是你的构造,我看不出可读性有任何降低 - 只要三行,如果值存在,就会被记录下来。如果这是用例,难怪设计师认为这是多余的。 -
使用 java9 你可以做
repository.findById(id).stream().peek(...).findAny().orElseThrow(...),但它仍然是完成你想要的事情的一种黑客方式。这个问题绝对值得一票,我也希望得到正确的答案
标签: java lambda java-8 optional java-9