How to implement the Elvis operator in Java 8?
Solution 1
Maybe I'm overlooking something, but is there a reason that you can't use Optional#map
?
The following example prints nothing, as Optional
is short-circuiting in the sense that, if the value inside the Optional
doesn't exist (it's null
or the Optional
is empty), it's treated as empty.
Optional.ofNullable("test")
.map(s -> null)
.ifPresent(System.out::println);
For that reason, I'd think you could just do the following:
return Optional.ofNullable(thing)
.map(x -> x.nullableMethod1(a))
.map(y -> y.nullableMethod2(b))
.map(Z::nullableMethod3);
This would map your thing
if it exists, or return an empty Optional
otherwise.
Solution 2
In Java 8, the Elvis operator can be simulated by chaining .map(...)
calls on an Optional.ofNullable(...)
and capping it with .orElse(...)
:
Optional.ofNullable(dataObject)
.map(DataObject::getNestedDataObject)
.map(NestedDataObject::getEvenMoreNestedDataObject)
...
.orElse(null);
A full example:
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
}
}
Mickalot
Updated on July 05, 2022Comments
-
Mickalot almost 2 years
I have the classic "Elvis operator" case, where I'm calling methods that each may return null and chaining them together:
thing?:nullableMethod1(a)?:nullableMethod2(b)?:nullableMethod3()
In Java 8, the most faithful implementation I've found is something like this:
return Optional.ofNullable(thing) .flatMap(x -> Optional.ofNullable(x.nullableMethod1(a))) .flatMap(y -> Optional.ofNullable(y.nullableMethod2(b))) .flatMap(z -> Optional.ofNullable(z.nullableMethod3()))
I wish that Java's
Optional
had something akin to the elvis operator:public<U> Optional<U> elvisOperator(Function<? super T, ? extends U> mapper) { return flatMap(t -> Optional.ofNullable(mapper.apply(t)); }
So that I wouldn't have to wrap each return value:
return Optional.ofNullable(thing) .elvisOperator(x -> x.nullableMethod1(a)) .elvisOperator(y -> y.nullableMethod2(b)) .elvisOperator(Z::nullableMethod3); // also nice
Is there a more efficient and idiomatic way to implement the Elvis operator pattern in Java 8?
-
Holger over 5 yearsIt’s worth pointing to the documentation of
Optional.map
to show that the behavior is intentional: “If the mapping function returns anull
result then this method returns an emptyOptional
”. -
melston over 5 years@Holger, and this explains why, in the long run, that was a bad idea. Or, at least, sub-optimal (from a FP perspective).
-
Mickalot about 5 yearsI think what you're adding to the accepted answer is that some languages blur the concepts of optionality and nullability in such a way that the Elvis operator can be used without needing to unpack the result at the end. In Java, the method implementations sometimes blur those concepts, but the type system has distinct notions, so yes, you're right that if one wanted a @Nullable Foo instead of an Optional<Foo>, one would likely want to use orElse at the end.
-
Abhishek Divekar about 5 years@Mickalot yes, that was the point I was trying to make. I had seen a few other answers and was confused by how to do it without an
Optional
, until I tried this example. I thought it might benefit others. -
yeoman over 3 yearsThey implemented a very important feature but for some reason decided to severely damage trusty old map in the process. It would be easier to forgive if they had actually named the necessary second method with this behavior "elvis" 🤣