问题描述
昨天我不得不写一段丑陋的代码,以便对对象的字段执行许多空检查,以避免三元运算符构造中的 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
中,如果 B
和 C
都是兼容的数字类型,但其中一个是装箱对象和另一个是原语,大多数人会认为结果是装箱的,通过自动装箱原语。
事实并非如此。相反,三元运算符将对象拆箱,因此它们都是基元,结果则是基元。
这意味着以下内容相同:
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
。
在问题中的情况下,B
是 Long.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*/);
,
- 代码段中没有
null
检查。 - 最好实现一个简单的实用方法,并在设置
cpu
和memory
时使用,如果"".equals(val)
为{{1,使用Joda条件val
来防止NPE }} - 为避免拆箱,请使用
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());