返回第一个非空值

2022-09-01 12:47:02

我有很多功能:

String first(){}
String second(){}
...
String default(){}

每个都可以返回一个 null 值,但默认值除外。每个函数可以采用不同的参数。例如,第一个可以不带参数,第二个可以接受字符串,第三个可以接受三个参数,依此类推。我想做的是这样的:

ObjectUtils.firstNonNull(first(), second(), ..., default());

问题在于,由于函数调用,这确实进行了预先评估。我想在哪里提前退出,比如在第二个函数之后(因为函数调用可能很昂贵,想想API调用等)。在其他语言中,您可以执行类似如下操作:

return first() || second() || ... || default()

在Java中,我知道我可以做这样的事情:

String value;
if (value = first()) == null || (value = second()) == null ...
return value;

这不是很可读的IMO,因为所有==空检查。ObjectUtils.firstNonNull() 首先创建一个集合,然后进行迭代,只要懒惰地评估函数,这是可以的。

建议?(除了做一堆如果)


答案 1
String s = Stream.<Supplier<String>>of(this::first, this::second /*, ... */)
                 .map(Supplier::get)
                 .filter(Objects::nonNull)
                 .findFirst()
                 .orElseGet(this::defaultOne);

它在第一个非空值处停止,否则设置从 返回的值。只要您保持顺序,您就是安全的。当然,这需要Java 8或更高版本。defaultOne

它在第一次出现非空值时停止的原因是由于 Stream 处理每个步骤的方式。地图中间操作过滤器也是如此。另一边的发现首先是短路端子操作。因此,它继续使用下一个元素,直到其中一个元素与过滤器匹配。如果没有匹配的元素,则返回空的可选选项,从而调用 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);

请注意,只有在您调用供应商时才会对其进行评估。通过这种方式,您可以获得懒惰的评估行为。供应商 lambda 表达式中的方法参数必须是最终参数或实际上是最终参数。get


答案 2

这可以通过流非常干净地完成。Suppliers

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

这有效的原因是,尽管外观如此,但整个执行都是由 驱动的,它从中拉出一个项目,它懒惰地从中拉取项目,哪个调用来处理每次拉取。 当一个项目通过过滤器时,将停止从流中拉出,因此后续供应商将不会打电话。findFirst()filter()map()get()findFirst()get()

虽然我个人认为声明式 Stream 样式更清晰,更具表现力,但如果您不喜欢该样式,则不必使用 Stream 来处理它: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();
}

很明显,如果您用尽了列表中的选项,则可以返回 一个,而不是返回,而是返回 null (yuk),一个默认字符串或抛出一个异常。OptionalString