为什么 Optional 不提供 peek 方法?
Why does Optional not provide a peek method?
我很想知道为什么 Java 的 Optional does not provide a peek
method similar to Stream
's one。
Stream
接口的 peek
方法 javadoc 指出:
- @apiNote This method exists mainly to support debugging, where you want to see the elements as they flow past a certain point in a pipeline
这几乎完全描述了我的用例:
@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
returns Optional<User>
(参见 CrudRepository#findById))
但它无法编译,因为 Optional
上没有 peek
方法。
所以如果没有 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
.
的 hack 而不是干净的使用
从 Java 9 开始,可以将 Optional
转换为 Stream
,但流没有 orElseThrow
方法(显然不应该)。
也可以使用 ifPresent
做同样的事情,但 returns void
。 (对我来说 ifPresent
似乎不应该 return 除了 void
之外的任何东西)
我在滥用 Optional
吗?
peek
方法的缺失是故意的吗? (但同时 Vavr 的 Option
does provide the peek
方法。)
或者只是被认为不值得?
好吧,只有设计人员才能回答 "exact" 详细信息,说明为什么 Optionals 没有 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))
.
已经有 Optional::ifPresent
method that accepts a Consumer
.
在Java8中,唯一的方法是使用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::ifPresent
和 Optional::isPresent
方法来记录结果。
但你可能想要一些东西。答案可能是疏忽。
我很想知道为什么 Java 的 Optional does not provide a peek
method similar to Stream
's one。
Stream
接口的 peek
方法 javadoc 指出:
- @apiNote This method exists mainly to support debugging, where you want to see the elements as they flow past a certain point in a pipeline
这几乎完全描述了我的用例:
@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
returns Optional<User>
(参见 CrudRepository#findById))
但它无法编译,因为 Optional
上没有 peek
方法。
所以如果没有 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
做同样的事情,但 returns void
。 (对我来说 ifPresent
似乎不应该 return 除了 void
之外的任何东西)
我在滥用 Optional
吗?
peek
方法的缺失是故意的吗? (但同时 Vavr 的 Option
does provide the peek
方法。)
或者只是被认为不值得?
好吧,只有设计人员才能回答 "exact" 详细信息,说明为什么 Optionals 没有 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))
已经有 Optional::ifPresent
method that accepts a Consumer
.
在Java8中,唯一的方法是使用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::ifPresent
和 Optional::isPresent
方法来记录结果。
但你可能想要一些东西。答案可能是疏忽。