为什么Optional不提供偷看方法?

时间:2018-12-02 13:40:15

标签: java lambda java-8 optional java-9

我很好奇为什么Java的Optional不提供类似于Stream's onepeek方法。

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方法。)

还是被认为不值得?

3 个答案:

答案 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))

see this answer for a similar example

答案 2 :(得分:1)

已经有Optional::ifPresentOptional::isPresent方法记录结果。 但是您可能想要符合要求的东西。答案可能是疏忽。