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

Java: how to avoid NPE in ternary operator, in general how to perform null-checks elegantly?

昨天我不得不写一段丑陋的代码,为了对对象的字段执行许多空检查,以避免三元运算符构造的 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..);

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

问题 1:有人可以提示我是否可以找到使用 Optional<T> 的方法来解决带有三元运算符的第一个变体中的 NPE?因为这个片段有效:!rt.getCpu().equals("")) ? Long.parseLong(rt.getCpu()) : null 即如果我明确地将 null 作为一个值,当条件满足时我得到 null

一般来说,有什么优雅的 Java 8+ 方法来处理这个问题吗?

问题 2:您如何优化出色的 if-else 结构以进行空值检查?

  1. 代码段中没有 null 检查。
  2. 最好实现一个简单的实用方法,在设置cpumemory时使用它,如果valnull
  3. 为了避免拆箱,使用 Long.valueOf 而不是 Long.parseLong which returns primitive 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());

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

public static Long getValue(Supplier<String> str, Supplier<Long> defVal) {
    return getValue(str.get(), defVal.get()); // calling implementation above 
}

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

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

事实并非如此。三元运算符改为拆箱对象,因此它们都是原语,结果也是原语。

这意味着以下是相同的:

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

Long R = ... ? B : C;

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

结果是,如果 C 为 null,则得到 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*/);