如何在Java 8中实现Elvis运算符?

时间:2018-09-12 00:27:04

标签: java java-8 functional-programming optional

我有一个经典的“猫王运算符”案例,我在其中调用每个可能返回null并将它们链接在一起的方法:

thing?:nullableMethod1(a)?:nullableMethod2(b)?:nullableMethod3()

在Java 8中,我发现的最可靠的实现是这样的:

return Optional.ofNullable(thing)
    .flatMap(x -> Optional.ofNullable(x.nullableMethod1(a)))
    .flatMap(y -> Optional.ofNullable(y.nullableMethod2(b)))
    .flatMap(z -> Optional.ofNullable(z.nullableMethod3()))

我希望Java的Optional类似于猫王运算符:

public<U> Optional<U> elvisOperator(Function<? super T, ? extends U> mapper) {
    return flatMap(t -> Optional.ofNullable(mapper.apply(t));
}

这样我就不必包装每个返回值:

return Optional.ofNullable(thing)
    .elvisOperator(x -> x.nullableMethod1(a))
    .elvisOperator(y -> y.nullableMethod2(b))
    .elvisOperator(Z::nullableMethod3); // also nice

在Java 8中是否有更高效,更惯用的方式实现Elvis运算符模式?

3 个答案:

答案 0 :(得分:13)

也许我正在忽略某些内容,但是是否有不能使用Optional#map的原因?

下面的示例不打印任何内容,因为Optional短路,即如果Optional内部的值不存在(它是{{1 }}或null为空),则将其视为空。

Optional

基于这个原因,我认为您可以执行以下操作:

Optional.ofNullable("test")
        .map(s -> null)
        .ifPresent(System.out::println);

这将映射您的return Optional.ofNullable(thing) .map(x -> x.nullableMethod1(a)) .map(y -> y.nullableMethod2(b)) .map(Z::nullableMethod3); (如果存在),否则返回一个空的thing

答案 1 :(得分:0)

在Java 8中,可以通过在.map(...)上链接Optional.ofNullable(...)调用并用.orElse(...)进行上限来模拟Elvis运算符:

Optional.ofNullable(dataObject)
.map(DataObject::getNestedDataObject)
.map(NestedDataObject::getEvenMoreNestedDataObject)
...
.orElse(null);

完整示例:

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

答案 2 :(得分:0)

我写了一个 compiler plugin,它在编译时操作 AST 以生成 Optional.ofNullable

它几乎没有限制,但确实减少了重复代码

PS:我是插件的作者