Java:如何避免三元运算符中的 NPE,通常如何优雅地执行空检查?

问题描述

昨天我不得不写一段丑陋的代码,以便对对象的字段执行许多空检查,以避免三元运算符构造中的 NPE。

有问题的一段代码

ResourceThresholds rt = getThresholdsFromConfig();
Thresholds defaultPerContainer = getDefaultThresholds();
    
return new Thresholds((!rt.getcpu().equals("")) ? Long.parseLong(rt.getcpu()) : defaultPerContainer.getcpu(),(!rt.getMemory().equals("")) ? Long.parseLong(rt.getMemory())  : defaultPerContainer.getMemory(),/*omitted for brevity*/);

我在 defaultPerContainer.getcpu() 获得 NPE,因为字段 cpu = null。 这很好,Java 以它的方式工作。 为什么我不只是认字段 Long cpu = 0L; ?因为我需要 null 值作为我们不设置任何值的指标。

这段特定代码的最终功能变体是:

        Long cpuVal;
        if (!rt.getcpu().equals("")) {
            cpuVal = Long.parseLong(rt.getcpu());
        } else {
            cpuVal = defaultPerContainer.getcpu();
        }
        Long memory;
        if (!rt.getMemory().equals("")) {
            memory = Long.parseLong(rt.getMemory());
        } else {
           memory = defaultPerContainer.getMemory();
        }
        //... many similar if-elses that give me the desired value;
        //which is really ugly,and I believe I am not the only one hitting this.
        return new Thresholds(cpuVal,memory..);

这段代码可以正常工作,但很丑!

Q1:有人可以提示我是否可以找到一种使用 Optional<T>解决一个变体中的 NPE 和三元运算符的方法吗?因为此代码段有效:!rt.getcpu().equals("")) ? Long.parseLong(rt.getcpu()) : null 即如果我明确将 null 作为值,则在满足条件时我会得到 null

总的来说,有没有优雅的 Java 8+ 方法来处理这个问题?

Q2:你如何优化用于空检查的辉煌 if-else 结构?

解决方法

问题在于,在三元表达式 A ? B : C 中,如果 BC 都是兼容的数字类型,但其中一个是装箱对象和另一个是原语,大多数人会认为结果是装箱的,通过自动装箱原语。

事实并非如此。相反,三元运算符将对象拆箱,因此它们都是基元,结果则是基元。

这意味着以下内容相同:

long B = ...;
Long C = ...;

Long R = ... ? B : C;

Long R = (Long) (... ? B : (long) C);

结果是,如果 C 为空,你得到 NPE。

修复它的一种方法是强制自动装箱 B

Long R = ... ? (Long) B : C;

通过这种更改,空 C 值将简单地设置 R = null

在问题中的情况下,BLong.parseLong(rt.getCpu()),因此不要添加强制转换来强制自动装箱,而是改用 long.valueOf(String s)

另外,不相关,用isEmpty()代替equals("")A不需要括号。

将代码更改为:

return new Thresholds(!rt.getCpu().isEmpty() ? Long.valueOf(rt.getCpu()) : defaultPerContainer.getCpu(),!rt.getMemory().isEmpty() ? Long.valueOf(rt.getMemory())  : defaultPerContainer.getMemory(),/*omitted for brevity*/);
,
  1. 代码段中没有 null 检查。
  2. 最好实现一个简单的实用方法,并在设置cpumemory时使用,如果"".equals(val)为{{1,使用Joda条件val来防止NPE }}
  3. 为避免拆箱,请使用 null 而不是 Long.valueOf,后者返回原始 Long.parseLong
long

还可以使用参数供应商提供重载实用程序方法,然后将方法引用传递给它:

public static Long getValue(String val,Long defaultValue) {
    return "".equals(val) ? defaultValue : Long.valueOf(val);
}

Long cpuVal = getValue(rt.getCpu(),defaultPerContainer.getCpu());
Long memory = getValue(rt.getMemory(),defaultPerContainer.getMemory());