使用 Java,可以安全地访问深度嵌套引用,例如 a.b.c.d.e
,我们通常必须在每个级别指定空检查或包装Optional
s并使用orElse()
。(与 Kotlin/C# 等语言不同,a?.b?.c?.d?.e
或类似语言都可以工作。
我想知道以下辅助方法是否是一个合理的选择:
public <T> T valueOrNull(Supplier<T> expression) {
try {
return expression.get();
} catch (NullPointerException e) {
return null;
}
}
然后可以安全地将其与value = valueOrNull(() -> a.b.c.d.e)
一起使用。
注意:我知道由于性能原因等原因,捕获NullPointerException
通常不受欢迎,但想知道这里的用法是否是一个合理的例外。
你可以写一个函数,它需要一个值和一个getter来转换它,就像这样
public static <T1, T2> T1 coalesce(T2 value, Function<T2,T1> f1){
return f1.apply(value);
}
然后你会这样称呼它coalesce(value, Clazz::getA)
.
对于链中的每一步,您的合并函数中都需要一个额外的函数,如下所示
public static <T1, T2, T3> T1 coalesce(T3 value, Function<T3,T2> f1, Function<T2,T1> f2){
T2 t2 = f1.apply(value);
if(t2 == null)
return null;
return f2.apply(t2);
}
对于两个深度,以及
public static <T1, T2, T3, T4> T1 coalesce(T4 value, Function<T4,T3> f1, Function<T3,T2> f2, Function<T2,T1> f3){
T3 t3 = f1.apply(value);
if(t3 == null)
return null;
T2 t2 = f2.apply(t3);
if(t2 == null)
return null;
return f3.apply(t2);
}
对于三个深度,依此类推,对于更深的深度。
示例代码:
A test1 = new A(null);
A test2 = new A(new B(null));
A test3 = new A(new B(new C(null)));
A test4 = new A(new B(new C("1234")));
System.out.println(coalesce(test1, A::getB, B::getC, C::getS));
System.out.println(coalesce(test2, A::getB, B::getC, C::getS));
System.out.println(coalesce(test3, A::getB, B::getC, C::getS));
System.out.println(coalesce(test4, A::getB, B::getC, C::getS));
System.out.println(coalesce(test2, A::getB));
System.out.println(coalesce(test3, A::getB, B::getC));
A 是具有类 B 成员的类,B 是具有类 C 成员的类,C 是具有类 String 成员的类,具有适当的 getter。输出符合预期:
null
null
null
1234
B@776ec8df
C@41629346
前三种情况下为 null,其中 C 中的字符串为 null,第四种情况下为字符串的值,第五种和第四种情况分别返回 B 和 C 类型的对象。
不幸的是,我没有看到使代码更短的方法。