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));
}
(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));
}
也可以这样做(见这个答案):
@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方法.)
或者它被认为不值得吗?
已经有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));
......应简化实施自己的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));
好吧,只有设计者才能回答你“确切”的细节,为什么Optional 没有 peek 方法。
所以,现在,你不得不使用isPresent()在我看来实际上看起来很好的方法:
if (userOptional.isPresent()) 
    logger.debug("Found user = {} with id = {}", userOptional.get(), id);
或者,如果您希望将其作为管道的一部分,您可以考虑链接页面上的建议答案。
顺便说一句,考虑到 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))