wake-up-neo.net

Warum bietet Optional keine Peek-Methode?

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?

7
caco3

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

sehen Sie diese Antwort für ein ähnliches Beispiel .

3
Aomine

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));
5
Nikolas

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.

1
fastcodejava