Ich bin neugierig zu wissen, warum Javas Optional keine peek
-Methode bereitstellt, die der Stream
-Methode ähnlich ist .
Die peek
-Methode javadoc der Stream
-Schnittstelle lautet:
- @apiNote Diese Methode unterstützt hauptsächlich das Debuggen, bei dem die Elemente angezeigt werden sollen, wenn sie an einem bestimmten Punkt in einer Pipeline vorbeiströmen
Dies beschreibt meinen Anwendungsfall fast genau:
@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
gibt Optional<User>
zurück (siehe CrudRepository # findById ))
Es wird jedoch nicht kompiliert, da es keine peek
-Methode für Optional
gibt.
Ohne die peek
-Methode wandelt sich also alles in:
@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));
}
Es ist auch möglich, so etwas zu tun (siehe diese Antwort ):
@NoArgsConstructor(access = PRIVATE)
public abstract class OptionalUtils {
public static <T> UnaryOperator<T> peek(Consumer<T> consumer) {
return t -> {
consumer.accept(t);
return t;
};
}
}
Und benutze es mit der map
Methode:
return repository.findById(id)
.map(OptionalUtils.peek(u -> logger.debug("Found user = {} with id = {}", u, id)))
.orElseThrow(() -> new UserNotFoundException("id = " + id));
Aber ich denke, das ist eher ein Hack als eine saubere Verwendung von Optional
.
Seit Java 9 ist es möglich, Optional
in Stream
umzuwandeln, aber der Stream verfügt nicht über die orElseThrow
-Methode (und sollte dies offensichtlich nicht tun).
Es ist auch möglich, dasselbe mit ifPresent
zu tun, aber es gibt void
zurück. (Und für mich scheint es, dass ifPresent
nichts anderes als void
zurückgeben soll.)
Missbrauche ich Optional
?
Ist das Fehlen der peek
-Methode beabsichtigt? (Gleichzeitig liefert Vavrs Option
jedoch die peek
-Methode.)
Oder es wurde einfach als nicht wert angesehen?
Nun, nur die Designer konnten Ihnen die "genauen" Details beantworten, weshalb es für Peerals keine Peek-Methode gab.
Im Moment bleiben Sie also mit isPresent()
, was aus meiner Sicht eigentlich gut erscheint:
if (userOptional.isPresent())
logger.debug("Found user = {} with id = {}", userOptional.get(), id);
sie können auch die vorgeschlagenen Antworten auf der verlinkten Seite berücksichtigen, wenn Sie sie als Teil der Pipeline verwenden möchten.
Übrigens, angesichts der neuen stream
-Methode ab JDK9 könnten Sie Folgendes tun:
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))
Es gibt bereits die Optional::ifPresent
Methode, die eine Consumer
akzeptiert.
In Java 8 besteht die einzige Möglichkeit darin, Optional::map
, um die Entität auf sich selbst abzubilden und als peek
-Methode zu verwenden:
return repository.findById(id)
.map(u -> {
logger.debug("Found user = {} with id = {}", u, id)
return u;
})
.orElseThrow(() -> new UserNotFoundException("id = " + id));
... die durch Implementierung einer eigenen peek
-Methode vereinfacht werden soll:
<T> UnaryOperator<T> peek(Consumer<T> consumer) {
return t -> {
consumer.accept(t);
return t;
};
}
... und komfortabel mit Optional
verwendet werden:
return repository.findById(id)
.map(this.peek(logger.debug("Found user = {} with id = {}", u, id)))
.orElseThrow(() -> new UserNotFoundException("id = " + id));
Es gibt bereits eine Optional::ifPresent
- und Optional::isPresent
-Methode, um das Ergebnis zu protokollieren. Aber Sie wollen wahrscheinlich etwas in einer Reihe. Die Antwort darauf ist wahrscheinlich ein Versehen.