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

u0njafvf  于 2022-10-30  发布在  Java
关注(0)|答案(5)|浏览(258)

我有一个经典的“Elvis operator”案例,我调用了每个都可能返回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具有类似于elvis操作符的功能:

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

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

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

在Java8中,有没有更有效、更惯用的方法来实现Elvis运算符模式?

9ceoxa92

9ceoxa921#

也许我忽略了一些东西,但是有什么原因不能使用Optional#map吗?
下面的示例不打印任何内容,因为Optionalshort-circuiting,即如果Optional中的值不存在(它是nullOptional为空),它将被视为空。

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);

这将Map您的thing(如果存在),否则返回空的Optional

xpcnnkqh

xpcnnkqh2#

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

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

ghg1uchk3#

我写了一个compiler plugin,它在编译时操纵AST生成Optional.ofNullable
它几乎没有限制,但确实减少了重复代码
PS:我是作者的插件

oyjwcjzk

oyjwcjzk4#

模拟elvis操作员的另一个选项是使用以下帮助器函数:

public static <T> T get(final Supplier<T> it) {
    try {
        return it.get();
    } catch (final NullPointerException e) {
        return null;
    }
}

您可以像这样使用它:

var x = get(() -> thing.nullableMethod1(a).nullableMethod2(b).nullableMethod3());
q1qsirdb

q1qsirdb5#

为了完成Jacob的想法,我创建了这个类:

public class OptionalUtils {
  public static <T, U> Optional<U> elvisOperator(T t, Function<? super T, ? extends U> mapper) {
    return elvisOperator(Optional.ofNullable(t), mapper);
  }

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

那你就做

elvisOperator(elvisOperator(dataObject, DataObject::getNestedDataObject)
    NestedDataObject::getEvenMoreNestedDataObject))...

相关问题