Je trouve cette question très intéressante car les appels chaînés avec potentiel null
retours sont une nuisance commune, et facultatif peut raccourcir la chaîne de vérification null habituelle beaucoup. Mais le problème est que la nature des méthodes de flux fonctionnels cache les valeurs intermédiaires dans les fonctions de mappage. L'imbrication est un moyen de les garder disponibles, mais peut aussi devenir ennuyeux si la longueur de la chaîne d'appel augmente, comme vous l'avez compris.
Je ne peux pas penser à une solution simple et légère, mais si la nature de votre projet conduit à ces situations régulièrement, cette classe util pourrait aider:
public static class ChainedOptional<T>
{
private final List<Object> intermediates;
private final Optional<T> delegate;
private ChainedOptional(List<Object> previousValues, Optional<T> delegate)
{
this.intermediates = new ArrayList<>(previousValues);
intermediates.add(delegate.orElse(null));
this.delegate = delegate;
}
public static <T> ChainedOptional<T> of(T value)
{
return of(Optional.ofNullable(value));
}
public static <T> ChainedOptional<T> of(Optional<T> delegate)
{
return new ChainedOptional<>(new ArrayList<>(), delegate);
}
public <R> ChainedOptional<R> map(Function<T, R> mapper)
{
return new ChainedOptional<>(intermediates, delegate.map(mapper));
}
public ChainedOptional<T> ifPresent(Consumer<T> consumer)
{
delegate.ifPresent(consumer);
return this;
}
public ChainedOptional<T> ifPresent(BiConsumer<List<Object>, T> consumer)
{
delegate.ifPresent(value -> consumer.accept(intermediates, value));
return this;
}
public <X extends Throwable> T orElseThrow(Supplier<? extends X> exceptionSupplier)
throws X
{
return delegate.orElseThrow(exceptionSupplier);
}
public <X extends Throwable> T orElseThrow(Function<List<Object>, X> exceptionSupplier)
throws X
{
return orElseThrow(() -> exceptionSupplier.apply(intermediates));
}
}
Vous pouvez l'utiliser en enroulant une option ou une plaine valeur. Lorsque vous utilisez ensuite la méthode map
pour chaîner des appels de méthode, il fournira un nouveau ChainedOptional tout en stockant la valeur actuelle dans une liste. À la fin (ifPresent
, orElseThrow
), vous obtiendrez non seulement la dernière valeur, mais aussi la liste de toutes les valeurs intermédiaires. Comme on ne sait pas combien d'appels seront enchaînés, je n'ai pas trouvé le moyen de stocker ces valeurs de manière sécurisée.
Voir exemples ici:
ChainedOptional.of(1)
.map(s -> s + 1)
.map(s -> "hello world")
.map(s -> (String) null)
.map(String::length)
.ifPresent((intermediates, result) -> {
System.out.println(intermediates);
System.out.println("Result: " + result);
})
.orElseThrow(intermediates -> {
System.err.println(intermediates);
return new NoSuchElementException();
});
// [1, 2, hello world, null, null]
// Exception in thread "main" java.util.NoSuchElementException
// at ...
ChainedOptional.of(1)
.map(s -> s + 1)
.map(s -> "hello world")
// .map(s -> (String) null)
.map(String::length)
.ifPresent((intermediates, result) -> {
System.out.println(intermediates);
System.out.println("Result: " + result);
})
.orElseThrow(intermediates -> {
System.err.println(intermediates);
return new NoSuchElementException();
});
// [1, 2, hello world, 11]
// Result: 11
Hope this helps. Faites-moi savoir si vous venez avec une meilleure solution.
Ils sont disponibles. Vous pouvez faire quelque chose avec a et b. Quel est le problème? – Oleg
'a' n'est pas disponible avec la méthode' flatMap'. Je veux éviter d'imbriquer 'ifPresent()' de telle sorte que 'a' et' b' soient tous deux disponibles dans la portée la plus interne et qu'une exception puisse être levée si l'une des imbrications échoue. – Zhro