J'ai le cas classique de "l'opérateur Elvis", où j'appelle des méthodes qui peuvent chacune retourner null et les enchaîne ensemble:
thing?:nullableMethod1(a)?:nullableMethod2(b)?:nullableMethod3()
Dans Java 8, l'implémentation la plus fidèle que j'ai trouvée est quelque chose comme ceci:
return Optional.ofNullable(thing)
.flatMap(x -> Optional.ofNullable(x.nullableMethod1(a)))
.flatMap(y -> Optional.ofNullable(y.nullableMethod2(b)))
.flatMap(z -> Optional.ofNullable(z.nullableMethod3()))
Je souhaite que le Optional
de Java ressemble à l'opérateur elvis:
public<U> Optional<U> elvisOperator(Function<? super T, ? extends U> mapper) {
return flatMap(t -> Optional.ofNullable(mapper.apply(t));
}
Pour ne pas avoir à encapsuler chaque valeur de retour:
return Optional.ofNullable(thing)
.elvisOperator(x -> x.nullableMethod1(a))
.elvisOperator(y -> y.nullableMethod2(b))
.elvisOperator(Z::nullableMethod3); // also Nice
Existe-t-il un moyen plus efficace et idiomatique d'implémenter le modèle d'opérateur Elvis dans Java 8?
Peut-être que j'oublie quelque chose, mais y a-t-il une raison pour laquelle vous ne pouvez pas utiliser Optional#map
?
L'exemple suivant n'affiche rien, car Optional
est court-circuitage dans le sens où, si la valeur à l'intérieur de Optional
n'existe pas (c'est null
ou Optional
est vide), il est traité comme vide.
Optional.ofNullable("test")
.map(s -> null)
.ifPresent(System.out::println);
Pour cette raison, je pense que vous pourriez simplement faire ce qui suit:
return Optional.ofNullable(thing)
.map(x -> x.nullableMethod1(a))
.map(y -> y.nullableMethod2(b))
.map(Z::nullableMethod3);
Cela mapperait votre thing
s'il existe, ou retournerait un Optional
vide sinon.
Dans Java 8, l'opérateur Elvis peut être simulé en chaînant .map(...)
appels sur un Optional.ofNullable(...)
et en le plafonnant avec .orElse(...)
:
Optional.ofNullable(dataObject)
.map(DataObject::getNestedDataObject)
.map(NestedDataObject::getEvenMoreNestedDataObject)
...
.orElse(null);
Un exemple complet:
import Java.util.Optional;
class Main {
// Data classes:
static class Animal {
Leg leg;
Animal(Leg leg) {
this.leg = leg;
}
Leg getLeg(){return this.leg;}
public String toString(){
String out = "This is an animal";
out += leg != null ? " with a leg" : "";
return out;
}
}
static class Leg {
Toes toes;
Leg(Toes toes) {
this.toes = toes;
}
Toes getToes(){return this.toes;}
public String toString(){
String out = "This is a leg";
out += toes != null ? " with a collection of toes" : "";
return out;
}
}
static class Toes {
Integer numToes;
Toes(Integer numToes) {
this.numToes = numToes;
}
Integer getNumToes(){return this.numToes;}
public String toString(){
String out = "This is a collection of ";
out += numToes != null && numToes > 0 ? numToes : "no";
out += " toes";
return out;
}
}
// A few example Elvis operators:
static Integer getNumToesOrNull(Animal a) {
return Optional.ofNullable(a)
.map(Animal::getLeg)
.map(Leg::getToes)
.map(Toes::getNumToes)
.orElse(null);
}
static Toes getToesOrNull(Animal a) {
return Optional.ofNullable(a)
.map(Animal::getLeg)
.map(Leg::getToes)
.orElse(null);
}
static Leg getLegOrNull(Animal a) {
return Optional.ofNullable(a)
.map(Animal::getLeg)
.orElse(null);
}
// Main function:
public static void main(String[] args) {
// Trying to access 'numToes':
System.out.println(getNumToesOrNull(new Animal(new Leg(new Toes(4))))); // 4
System.out.println(getNumToesOrNull(new Animal(new Leg(new Toes(null))))); // null
System.out.println(getNumToesOrNull(new Animal(new Leg(null)))); // null
System.out.println(getNumToesOrNull(new Animal(null))); // null
System.out.println(getNumToesOrNull(null)); // null
// Trying to access 'toes':
System.out.println(getToesOrNull(new Animal(new Leg(new Toes(4))))); // This is a collection of 4 toes
System.out.println(getToesOrNull(new Animal(new Leg(new Toes(null))))); // This is a collection of no toes
System.out.println(getToesOrNull(new Animal(new Leg(null)))); // null
System.out.println(getToesOrNull(new Animal(null))); // null
System.out.println(getToesOrNull(null)); // null
// Trying to access 'leg':
System.out.println(getLegOrNull(new Animal(new Leg(new Toes(4))))); // This is a leg with a collection of toes
System.out.println(getLegOrNull(new Animal(new Leg(new Toes(null))))); // This is a leg with a collection of toes
System.out.println(getLegOrNull(new Animal(new Leg(null)))); // This is a leg
System.out.println(getLegOrNull(new Animal(null))); // null
System.out.println(getLegOrNull(null)); // null
}
}