web-dev-qa-db-fra.com

Pourquoi Facultatif ne fournit-il pas une méthode de peek?

Je suis curieux de savoir pourquoi la méthode Facultatif de Java ne fournit pas une méthode peek semblable à celle de Stream .

La méthode peekjavadoc de l'interface Stream indique:

  • @apiNote Cette méthode existe principalement pour prendre en charge le débogage, où vous souhaitez voir les éléments lorsqu'ils dépassent un certain point dans un pipeline.

Ceci décrit presque exactement mon cas d'utilisation:

@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 renvoie Optional<User> (voir CrudRepository # findById ))

Mais il ne compilera pas car il n'y a pas de méthode peek sur Optional.

Donc, sans la méthode peek, tout ce qui précède se transforme en:

@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));
}

Il est également possible de faire quelque chose comme ceci (voir ceci réponse ):

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

Et utilisez-le avec la méthode map:

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

Mais je pense que ceci est un hack plutôt qu'un usage propre de Optional.

Depuis Java 9, il est possible de transformer Optional en Stream mais le flux n’a pas la méthode orElseThrow (et il ne devrait évidemment pas en être ainsi).

De plus, il est possible de faire la même chose en utilisant ifPresent mais il retourne void. (Et pour moi, il semble que ifPresent ne retourne rien d'autre que void)

Est-ce que j'utilise mal Optional

L'absence de la méthode peek est-elle intentionnelle? (Mais en même temps, Option de Vavr fournit la méthode peek .)

Ou était-ce juste considéré comme ne valant pas la peine?

7
caco3

Eh bien, seuls les concepteurs peuvent vous répondre avec les détails "exacts" expliquant pourquoi il n’existait pas de méthode Peek pour Optionals. 

Donc, pour l'instant, vous êtes coincé avec l'utilisation de isPresent(), qui me semble tout à fait bien:

if (userOptional.isPresent()) 
    logger.debug("Found user = {} with id = {}", userOptional.get(), id);

ou vous pouvez envisager les réponses suggérées sur la page liée si vous le souhaitez dans le cadre du pipeline.

en passant, étant donné la nouvelle méthode stream à partir de JDK9, vous pourriez faire:

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

voir cette réponse pour un exemple similaire .

3
Aomine

Il existe déjà la méthode Optional::ifPresent qui accepte une Consumer .

En Java 8, le seul moyen est d’utiliser Optional::map , pour mapper l’entité sur elle-même et l’utiliser comme méthode peek:

return repository.findById(id)
                 .map(u -> {
                     logger.debug("Found user = {} with id = {}", u, id)
                     return u;
                 })
                 .orElseThrow(() -> new UserNotFoundException("id = " + id));

... qui doit être simplifié en implémentant une méthode peek propre:

<T> UnaryOperator<T> peek(Consumer<T> consumer) {
    return t -> {
        consumer.accept(t);
        return t;
    };
}

... et utilisé confortablement avec Optional :

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

Il existe déjà des méthodes Optional::ifPresent et Optional::isPresent pour enregistrer le résultat. Mais vous voulez probablement quelque chose en ligne. La réponse à cette question est probablement un oubli.

1
fastcodejava