代码之家  ›  专栏  ›  技术社区  ›  Denis Zavedeev Brian Kelly

为什么Optional不提供Peek方法?

  •  7
  • Denis Zavedeev Brian Kelly  · 技术社区  · 6 年前

    我很想知道为什么JAVA的 Optional 不提供 peek 方法类似于 Stream 's one .

    这个 偷看 方法 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 .

    所以没有 偷看 方法以上所有内容转换为:

    @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));
    

    但我认为这是一种黑客行为,而不是 可选的 .

    自Java 9以来,有可能进行转换。 可选的 河流 但是小溪没有 orElseThrow 方法(显然不应该)。

    同样,也可以使用 ifPresent 但它回来了 void . (对我来说 如果存在 除了 无效 )

    我误用了吗? 可选的 ?

    是否缺少 偷看 有意的方法?(但同时,Vavr的 Option 是否提供 peek 方法。

    或者只是被认为不值得?

    3 回复  |  直到 6 年前
        1
  •  3
  •   Ousmane D.    6 年前

    好吧,只有设计人员才能回答你关于为什么选项没有peek方法的“确切”细节。

    所以,现在,你一直在使用 isPresent() 在我看来,这似乎很好:

    if (userOptional.isPresent()) 
        logger.debug("Found user = {} with id = {}", userOptional.get(), id);
    

    或者,如果您希望链接页面上的建议答案作为管道的一部分,也可以考虑它。

    顺便说一句,考虑到新的 stream 方法从JDK9开始,您可以执行以下操作:

    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
  •  5
  •   Nikolas Charalambidis    6 年前

    已经有了 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));
    

    …应简化实施自己的 偷看 方法:

    <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));
    
        3
  •  1
  •   fastcodejava    6 年前

    已经有了 Optional::ifPresent Optional::isPresent 方法来记录结果。 但你可能想要一些合适的东西。答案可能是疏忽。