How to implement the Elvis operator in Java 8?

Viewed 26119

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?

3 Answers

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.

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

There is one compiler plugin I have written which manipulate AST to generate Optional.ofNullable chain in compile time

It has few limitations but it does reduce repetitive code

PS: I am author of plugin

Related