【问题标题】:Alternative for Null Coalescing Operator空合并运算符的替代方案
【发布时间】:2017-05-17 08:19:46
【问题描述】:

使用 Java,可以安全地访问深层嵌套引用,例如 a.b.c.d.e,我们通常必须在每个级别指定空检查或包装在Optionals 中并使用orElse()。 (与 a?.b?.c?.d?.e 或类似工作的 Kotlin/C# 等语言不同。

我想知道以下辅助方法是否是一个合理的替代方法:

public <T> T valueOrNull(Supplier<T> expression) {
    try {
        return expression.get();
    } catch (NullPointerException e) {
        return null;
    }
}

这可以安全地与value = valueOrNull(() -&gt; a.b.c.d.e)一起使用。

注意:我知道捕获 NullPointerExceptions 通常会因为性能等原因而受到反对,但我想知道这里的用法是否是一个合理的例外。

【问题讨论】:

  • 捕获NullPointerExceptions 与性能无关,与一般代码质量有很大关系。
  • 有人甚至会争辩说,必须写a.b.c.d.e(或a?.b?.c?.d?.e)已经表明设计可疑(并不是说我不欢迎这样的运算符,但我倾向于得到更少的嵌套)。
  • @Kayaman 我提到了性能,因为对控制流使用异常通常比简单的分支要慢得多。如果仅将此方法隔离到此方法,则代码质量不会成为太大问题。关于您的第二条评论,在大多数情况下这可能是正确的,但考虑一个表示 XML 模式的 POJO,您在其中使用 JAXB 或类似的东西进行转换。必须访问深度嵌套的 xml 节点是有效的,您无法确定路径的有效性。
  • 这比使用Optional 的灵活性要低得多,而且我认为稍微减少的冗长不值得引入这样的实用程序。
  • @Hulk 我不同意这两点。我可以想象,当使用深度嵌套的 if 子句重构遗留代码时,这将是一个有用的工具。此外,这不是作为Optional 的替代品提供的,而是作为补充提供的。

标签: java nullpointerexception null null-coalescing-operator


【解决方案1】:

你可以写一个函数,它接受一个值和一个 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

前三种情况为空,C中的字符串为空,第四种有字符串的值,第五和第六种分别返回B和C类型的对象。

不幸的是,我没有办法让代码更短。

【讨论】:

  • OP的方案肯定更灵活更简单。
  • OP 的解决方案也将异常用于控制流并静默捕获异常。我只是在 java 8 中添加了另一种方法。
  • “静默”捕捉异常是一个特性,而不是一个负面问题。
  • 除非 NPE 发生自空指针上的方法调用以外的其他事物。如果任何内部方法抛出合法的 NPE,这可能会导致问题。
  • 没错,但大概内部方法是简单的 getter。当然,您可以滥用该构造,但没有办法阻止人们编写糟糕、丑陋或有害的代码。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2019-01-14
  • 1970-01-01
  • 2011-12-29
  • 1970-01-01
  • 1970-01-01
  • 2012-09-19
  • 2013-02-10
相关资源
最近更新 更多