我很好奇为什么Java的Optional不提供类似于Stream
's one的peek
方法。
peek
接口的Stream
方法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))
但是由于peek
上没有Optional
方法,因此无法编译。
因此,如果没有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
方法。)
还是被认为不值得?
答案 0 :(得分:5)
已经有一种Optional::ifPresent
方法可以接受Consumer
。
在Java 8中,唯一的方法是使用Optional::map
,将实体映射到自身,并将其用作peek
方法:
return repository.findById(id)
.map(u -> {
logger.debug("Found user = {} with id = {}", u, id)
return u;
})
.orElseThrow(() -> new UserNotFoundException("id = " + id));
...应简化为实现自己的peek
方法:
<T> UnaryOperator<T> peek(Consumer<T> consumer) {
return t -> {
consumer.accept(t);
return t;
};
}
...并与Optional
配合使用:
return repository.findById(id)
.map(this.peek(logger.debug("Found user = {} with id = {}", u, id)))
.orElseThrow(() -> new UserNotFoundException("id = " + id));
答案 1 :(得分:3)
好吧,只有设计人员才能回答您“为何”没有“偷看”方法的“确切”细节。
因此,就目前而言,您仍然无法使用isPresent()
,在我看来,这似乎还不错:
if (userOptional.isPresent())
logger.debug("Found user = {} with id = {}", userOptional.get(), id);
或者,如果您希望将其作为管道的一部分,则可以在链接页面上考虑建议的答案。
btw,鉴于自JDK9起采用了新的stream
方法,您可以执行以下操作:
return repository.findById(id) // Optional<User>
.stream() // Stream<User>
.peek(u -> logger.debug("Found user = {} by id = {}", u, id)) // Stream<User>
.findFirst() // Optional<User>
.orElseThrow(() -> new UserNotFoundException("id = " + id))
答案 2 :(得分:1)
已经有Optional::ifPresent
和Optional::isPresent
方法记录结果。
但是您可能想要符合要求的东西。答案可能是疏忽。