返回第一个非空值

lor*_*llo 21 java coalesce lazy-evaluation

我有很多功能:

String first(){}
String second(){}
...
String default(){}
Run Code Online (Sandbox Code Playgroud)

每个都可以返回一个空值,默认值除外.每个功能可以采用不同的参数.例如,第一个可以不带参数,第二个可以接受一个字符串,第三个可以接受三个参数,等等.我想做的是:

ObjectUtils.firstNonNull(first(), second(), ..., default());
Run Code Online (Sandbox Code Playgroud)

问题在于,由于函数调用,这需要进行急切的评估.我想早点退出,在第二个函数之后说(因为函数调用可能很昂贵,想想API调用等).在其他语言中,您可以执行与此类似的操作:

return first() || second() || ... || default()
Run Code Online (Sandbox Code Playgroud)

在Java中,我知道我可以做类似的事情:

String value;
if (value = first()) == null || (value = second()) == null ...
return value;
Run Code Online (Sandbox Code Playgroud)

由于所有的== null检查,这不是非常易读的IMO.ObjectUtils.firstNonNull()首先创建一个集合,然后迭代,只要该函数被懒惰地评估,这是可以的.

建议?(除了做一堆ifs)

Rol*_*and 35

String s = Stream.<Supplier<String>>of(this::first, this::second /*, ... */)
                 .map(Supplier::get)
                 .filter(Objects::nonNull)
                 .findFirst()
                 .orElseGet(this::defaultOne);
Run Code Online (Sandbox Code Playgroud)

它在第一个非null值上停止,或者设置从中返回的值defaultOne.只要你保持顺序,你就是安全的.当然这需要Java 8或更高版本.

它在第一次出现非空值时停止的原因是Stream每个步骤处理的方式.这map是一个中间操作,也是filter.的findFirst另一侧是一个短路端子的操作.所以它继续下一个元素,直到一个匹配过滤器.如果没有元素匹配,则返回空的可选项,因此orElseGet调用-supplier.

this::first等等只是方法参考.如果它们是静电的,请用它YourClassName::first等替换它.

如果您的方法的签名不同,这是一个示例:

String s = Stream.<Supplier<String>>of(() -> first("takesOneArgument"),
                                       () -> second("takes", 3, "arguments")
                                   /*, ... */)
                 .map(Supplier::get)
                 .filter(Objects::nonNull)
                 .findFirst()
                 .orElseGet(this::defaultOne);
Run Code Online (Sandbox Code Playgroud)

请注意,Supplier仅在您呼叫时评估get.这样你就可以获得懒惰的评估行为.supplier-lambda-expression中的方法参数必须是最终的或有效的final.

  • @EdwinBuck为何会成为障碍?这是Stream正在使用它的擅长的一个干净的例子. (2认同)
  • @AndriyKryvtsun我们想要懒惰的评估,供应商确保只在我们调用`get`时才进行评估.如果你要编写`Stream.of(first(),second())`那么两个方法都会在`Stream`构造之前执行. (2认同)

sli*_*lim 9

这可以用干净的流完成Suppliers.

Optional<String> result = Stream.<Supplier<String>> of(
     () -> first(), 
     () -> second(),
     () -> third() )
  .map( x -> x.get() )
  .filter( s -> s != null)
  .findFirst(); 
Run Code Online (Sandbox Code Playgroud)

这样做的原因在于,尽管有外观,但整个执行都是由findFirst()一个项目驱动的,它从filter()一个懒惰地拉出项目map(),从而调用get()处理每个拉动.findFirst()当一个项目通过过滤器时,将停止从流中拉出,因此后续供应商将不会get()调用.

虽然我个人觉得声明流风格更清洁,更表现,你不具备使用流与合作Supplier■如果你不喜欢的风格:

Optional<String> firstNonNull(List<Supplier<String>> suppliers {
    for(Supplier<String> supplier : suppliers) {
        String s = supplier.get();
        if(s != null) {
            return Optional.of(s);
        }
    }
    return Optional.empty();
}
Run Code Online (Sandbox Code Playgroud)

显而易见的是,如果您从列表中耗尽选项,那么如果不返回,Optional您可以同样返回a String,返回null(yuk),默认字符串或抛出异常.


Rya*_*n S -3

您可以通过反射来完成此操作:

public Object getFirstNonNull(Object target, Method... methods) {
    Object value = null;
    for (Method m : methods) {
        if ( (value = m.invoke(target)) != null) {
            break;
        }
    }
    return value;
}
Run Code Online (Sandbox Code Playgroud)