避免使用Optionals分支null检查

sla*_*hms 2 java optional java-8

假设我有下一段代码:

void doSmtng(A a){
    if (a!=null){
        B b = a.getB();
        if (b != null){
            C c = b.getC();
            if (c != null){
                d = c.getD();
            }
        }
    }
    doSmtngWithD(d);
}
Run Code Online (Sandbox Code Playgroud)

让我们说,我可以改变的,而不是获得API A,B,C,D我可以得到Optional<A>,Optional<B>等等.

编写更美观的代码以阻止所有这些空检查的正确方法是什么?另外,一旦变量(a,b,c,d ..)中的一个为空,有没有办法按照我的要求添加抛出异常?

Rol*_*and 6

另一种Optional方式,Holger在评论中也提到了这一点:

D d = Optional.ofNullable(a)
              .map(A::getB)
              .map(B::getC)
              .map(C::getD)
              .orElseThrow(/* your exception */);
Run Code Online (Sandbox Code Playgroud)

这可以按原样应用于您的代码.您无需重写访问器方法.null返回第一个值后抛出异常.但是当抛出异常时你不知道究竟是哪一个null.

如果你真的想知道这一点,你可能更好地实现一个简单的方法,它只是你的null-checks而不是使用Optional,例如

static <T> T mapIfNotNull(T value) {
  if (value == null) {
    // throw your exception...
  }
  return value;
}
Run Code Online (Sandbox Code Playgroud)

并在没有您的条件的情况下使用它如下:

A a = ...
B b = mapIfNotNull(a.getB());
C c = mapIfNotNull(b.getC());
D d = mapIfNotNull(c.getD());
Run Code Online (Sandbox Code Playgroud)

通过这种方式,您可以在每个步骤中抛出所需的异常...(例如,如果您提供了另外的参数,您可以在其中提供自定义异常mapIfNotNull(T value, Supplier<RuntimeException> exceptionSupplier)).或者只是Objects.requireNonNull在评论中使用Holger状态,如果NullPointerException满足您的需要.

如果您还想检查,您调用getXXon 的对象是否为null,则可能需要使用如下方法:

static <S, T> T map(S source, Function<S, T> mapFunction) {
  if (source == null) { // or again: Objects.requireNonNull(source,..)
    // throw exception due to source
  }
  T value = mapFunction.apply(source);
  if (value == null) { // or Objects.requireNonNull(value,..)
    // throw exception due to missing value
  }
  return value;
}
Run Code Online (Sandbox Code Playgroud)

呼叫工作几乎相同:

A a = ...
B b = map(a, A::getB);
C c = map(b, B::getC);
D d = map(c, C::getD);
Run Code Online (Sandbox Code Playgroud)

  • 请注意,您的`mapIfNotNull` [已存在](https://docs.oracle.com/javase/8/docs/api/java/util/Objects.html#requireNonNull-T-java.lang.String-).所以你可以使用`a = Objects.requireNonNull(a,"a"); B b = Objects.requireNonNull(a.getB(),"b"); C c = Objects.requireNonNull(b.getC(),"c"); D d = Objects.requireNonNull(c.getD(),"d");` (3认同)

khe*_*ood 5

从以下开始Optional<A> a:

a.flatMap(A::getOptionalB)
   .flatMap(B::getOptionalC)
   .flatMap(C::getOptionalD)
   .ifPresent(d -> doSomethingWithD(d));
Run Code Online (Sandbox Code Playgroud)

如果您想丢失其中一个异常,则抛出异常:

D d = a.flatMap(A::getOptionalB)
       .flatMap(B::getOptionalC)
       .flatMap(C::getOptionalD)
       .orElseThrow(() -> new SomeKindOfException());
doSomethingWithD(d);
Run Code Online (Sandbox Code Playgroud)

  • @slashms:不,这是未更改的方法,即可能返回`null`.参见[`Optional.map`](https://docs.oracle.com/javase/8/docs/api/java/util/Optional.html#map-java.util.function.Function-):"*If如果存在值,则将提供的映射函数应用于该值,如果结果为非null,则返回描述结果的Optional.否则返回一个空的Optional*"...... (3认同)
  • 我知道了.但它比仅保持方法原样和使用更复杂*,例如`Optional.ofNullable(a).map(A :: getB).map(B :: getC).map(C :: getD). ifPresent(此:: doSmtngWithD);` (2认同)