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

cac*_*co3 7 java lambda optional java-8 java-9

我很好奇,想知道为什么Java的可选没有提供peek类似的方法Stream的一个.

peek方法的Javadoc所述的Stream界面态:

  • @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));
}
Run Code Online (Sandbox Code Playgroud)

(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));
}
Run Code Online (Sandbox Code Playgroud)

也可以这样做(见这个答案):

@NoArgsConstructor(access = PRIVATE)
public abstract class OptionalUtils {
    public static <T> UnaryOperator<T> peek(Consumer<T> consumer) {
        return t -> {
            consumer.accept(t);
            return t;
        };
    }
}
Run Code Online (Sandbox Code Playgroud)

并使用它与map方法:

return repository.findById(id)
    .map(OptionalUtils.peek(u -> logger.debug("Found user = {} with id = {}", u, id)))
    .orElseThrow(() -> new UserNotFoundException("id = " + id));
Run Code Online (Sandbox Code Playgroud)

但我认为这是一个黑客而不是干净利用Optional.

从Java 9开始,可以转换OptionalStream但流不具备该orElseThrow方法(显然它不应该).

也可以使用相同ifPresent但返回void.(对我来说似乎不ifPresent应该返回任何其他东西void)

我在滥用Optional吗?

这种peek方法的缺失是故意的吗?(但与此同时,Vavr Option确实提供了这种peek方法.)

或者它被认为不值得吗?

Nik*_*las 6

已经有Optional::ifPresent接受a 的方法了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));
Run Code Online (Sandbox Code Playgroud)

......应简化实施自己的peek方法:

<T> UnaryOperator<T> peek(Consumer<T> consumer) {
    return t -> {
        consumer.accept(t);
        return t;
    };
}
Run Code Online (Sandbox Code Playgroud)

......并且舒适地使用Optional:

return repository.findById(id)
                 .map(this.peek(logger.debug("Found user = {} with id = {}", u, id)))
                 .orElseThrow(() -> new UserNotFoundException("id = " + id));
Run Code Online (Sandbox Code Playgroud)


Ous*_* D. 4

好吧,只有设计者才能回答你“确切”的细节,为什么Optional 没有 peek 方法。

所以,现在,你不得不使用isPresent()在我看来实际上看起来很好的方法:

if (userOptional.isPresent()) 
    logger.debug("Found user = {} with id = {}", userOptional.get(), id);
Run Code Online (Sandbox Code Playgroud)

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

顺便说一句,考虑到 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))
Run Code Online (Sandbox Code Playgroud)

请参阅此答案以获取类似的示例

  • 使用 ifPresent() 会更优雅:不需要调用 get()。 (4认同)