如何在 Java 中获得第一个非空值?

是否有与 SQL 的 COALESCE函数相当的 Java 语言?也就是说,有没有办法返回多个变量的第一个非空值?

例如:。

Double a = null;
Double b = 4.4;
Double c = null;

我想以某种方式拥有一个语句,它将返回 abc的第一个非空值——在本例中,它将返回 b或4.4。(类似于 sql 方法-return COALESCE(a,b,c))。我知道我可以明确地做到这一点,比如:

return a != null ? a : (b != null ? b : c)

但我想知道是否有任何内置的、可接受的功能来实现这一点。

138173 次浏览

不,没有。

你能得到的最接近的答案是:

public static <T> T coalesce(T ...items) {
for(T i : items) if(i != null) return i;
return null;
}

基于有效率的理由,你可以处理下列常见个案:

public static <T> T coalesce(T a, T b) {
return a == null ? b : a;
}
public static <T> T coalesce(T a, T b, T c) {
return a != null ? a : (b != null ? b : c);
}
public static <T> T coalesce(T a, T b, T c, T d) {
return ...
}
Object coalesce(Object... objects)
{
for(Object o : object)
if(o != null)
return o;
return null;
}

根据 LES2的回答,您可以通过调用重载函数来消除高效版本中的一些重复:

public static <T> T coalesce(T a, T b) {
return a != null ? a : b;
}
public static <T> T coalesce(T a, T b, T c) {
return a != null ? a : coalesce(b,c);
}
public static <T> T coalesce(T a, T b, T c, T d) {
return a != null ? a : coalesce(b,c,d);
}
public static <T> T coalesce(T a, T b, T c, T d, T e) {
return a != null ? a : coalesce(b,c,d,e);
}

如果只有两个变量需要检查,并且您使用的是番石榴,那么您可以使用 FirstNonNull (T first,T second)

这种情况需要一些预处理器。因为如果编写一个函数(静态方法)来选择第一个非空值,它会计算所有项。如果某些项是方法调用(可能是耗费时间的方法调用) ,那么就有问题了。即使在这些方法之前的任何项不为空,也会调用这些方法。

像这样的功能

public static <T> T coalesce(T ...items) …

应该使用,但在编译成字节码之前,应该有一个预处理器,它可以找到这个“合并函数”的用法,并将其替换为

a != null ? a : (b != null ? b : c)

更新2014-09-02:

多亏了 Java8和 Lambdas,在 Java 中才有可能实现真正的联合!包括关键的特性: 特定的表达式仅在需要的时候计算-如果早期的表达式不为空,那么后面的表达式就不计算(方法不调用,计算或磁盘/网络操作不执行)。

我写了一篇关于它的文章 Java 8: 聚合-hledáme neNULLové hodnoty-(用捷克语写的,但我希望代码示例对每个人来说都是可以理解的)。

你可以用番石榴做:

Optional.fromNullable(a).or(b);

如果 ab都是 null,它不会抛出 NPE。

编辑: 我错了,它确实抛出 NPE。 Michal izmazia评论的正确方式是:

Optional.fromNullable(a).or(Optional.fromNullable(b)).orNull();

如果只有两个引用要测试,并且您正在使用 Java8,那么您可以使用

Object o = null;
Object p = "p";
Object r = Optional.ofNullable( o ).orElse( p );
System.out.println( r );   // p

如果您导入静态可选的表达式是不是太糟糕。

不幸的是,“多个变量”的情况下,不可能使用可选的方法。相反,您可以使用:

Object o = null;
Object p = null;
Object q = "p";


Optional<Object> r = Stream.of( o, p, q ).filter( Objects::nonNull ).findFirst();
System.out.println( r.orElse(null) );   // p

只是为了完整起见,“几个变量”的情况确实是可能的,尽管一点也不优雅。例如,对于变量 opq:

Optional.ofNullable( o ).orElseGet(()-> Optional.ofNullable( p ).orElseGet(()-> q ) )

请注意,使用 orElseGet()注意到的情况下,opq不是变量,但表达式要么昂贵或不希望的副作用。

在最一般的情况下 coalesce(e[1],e[2],e[3],...,e[N])

coalesce-expression(i) ==  e[i]  when i = N
coalesce-expression(i) ==  Optional.ofNullable( e[i] ).orElseGet(()-> coalesce-expression(i+1) )  when i < N

这可能会生成过长的表达式。然而,如果我们试图移动到一个没有 null的世界,那么 v[i]很可能已经是 Optional<String>型,而不是简单的 String型。在这种情况下,

result= o.orElse(p.orElse(q.get())) ;

或在表达式的情况下:

result= o.orElseGet(()-> p.orElseGet(()-> q.get() ) ) ;

此外,如果您还要转向函数式声明样式,那么 opq的类型应该是 Supplier<String>,如下所示:

Supplier<String> q= ()-> q-expr ;
Supplier<String> p= ()-> Optional.ofNullable(p-expr).orElseGet( q ) ;
Supplier<String> o= ()-> Optional.ofNullable(o-expr).orElseGet( p ) ;

然后整个 coalesce简单地减少到 o.get()

举个更具体的例子:

Supplier<Integer> hardcodedDefaultAge= ()-> 99 ;
Supplier<Integer> defaultAge= ()-> defaultAgeFromDatabase().orElseGet( hardcodedDefaultAge ) ;
Supplier<Integer> ageInStore= ()-> ageFromDatabase(memberId).orElseGet( defaultAge ) ;
Supplier<Integer> effectiveAge= ()-> ageFromInput().orElseGet( ageInStore ) ;

defaultAgeFromDatabase()ageFromDatabase()ageFromInput()自然已经返回了 Optional<Integer>

如果我们满意 Supplier<Integer>,那么 coalesce就会变成 effectiveAge.get()或者简单的 effectiveAge

恕我直言,在 Java8中,我们将看到越来越多这样的代码结构,因为它同时具有极强的自解释性和高效性,特别是在更复杂的情况下。

我确实遗漏了一个类 Lazy<T>,它只调用 Supplier<T>一次,但是很懒惰,而且在 Optional<T>的定义中也缺乏一致性(即 Optional<T>-Optional<T>操作符,甚至是 Supplier<Optional<T>>)。

这个怎么样:

firstNonNull = FluentIterable.from(
Lists.newArrayList( a, b, c, ... ) )
.firstMatch( Predicates.notNull() )
.or( someKnownNonNullDefault );

JavaArrayList 方便地允许 null 条目,并且无论要考虑的对象数目如何,这个表达式都是一致的。(在这种形式下,所有被认为是同一类型的对象。)

你可以试试这个:

public static <T> T coalesce(T... t) {
return Stream.of(t).filter(Objects::nonNull).findFirst().orElse(null);
}

基于 这个响应

如果您想避免评估某些昂贵的方法,那么使用供应商怎么样?

像这样:

public static <T> T coalesce(Supplier<T>... items) {
for (Supplier<T> item : items) {
T value = item.get();
if (value != null) {
return value;
}
return null;
}

然后像这样使用它:

Double amount = coalesce(order::firstAmount, order::secondAmount, order::thirdAmount)

您还可以对具有两个、三个或四个参数的调用使用重载方法。

此外,您还可以使用流来实现以下功能:

public static <T> T coalesce2(Supplier<T>... s) {
return Arrays.stream(s).map(Supplier::get).filter(Objects::nonNull).findFirst().orElse(null);
}

从 Java9开始,就有内置的 Objects.requireNonNullElse方法用于两个参数的合并,这对我来说是最有用的。