利用 try catch 块而不是重复进行空检查
Taking advantage of try catch block instead of doing repeated null checks
今天我正在考虑从我的代码中删除不必要的 null
条件检查,主要涉及深层嵌套对象。
例如:
我有一个 class Big
并且它引用了 Small
对象及其 getter 和 setter,同样 Small
引用了
Tiny
对象及其 getter 和 setter 以及 Tiny
对象具有 tinyLength
变量,它是一个 Integer
变量。
因此,例如,如果我正在编写一个接受 Big
对象参数并且最后 returns tinyLength
的方法,我通常会做一些事情
像这样:
public Integer getTinyLength (Big big) {
if (big!=null && big.getSmall()!=null && big.getSmall().getTiny()!=null &&
big.getSmall().getTiny().getTinyLength()!=null) {
return big.getSmall().getTiny().getTinyLength();
}
else {
// throw exception or return null or do whatever
}
所以我的问题不是做所有这些垃圾,为什么我不做类似的事情:
try {
return big.getSmall().getTiny().getTinyLength();
}
catch (Exception e){
//null pointer exception being a subclass of Exception class
log your error here
throw new NullPointerException();
}
我觉得第二个代码片段非常简洁明了。这样做有什么缺点吗?
特别是当有多个嵌套对象时,取消对每个嵌套对象的引用通常会导致代码高度不可读/如果您错过其中一个对象也容易出错,并导致空值。
第一个代码示例效率低下,并且线程不安全。 getSmall()
被调用了四次,getTiny()
被调用了三次,getTinyLength()
被调用了两次。
第二个代码示例很危险。当您只想捕获 NullPointerException
时捕获 Exception
可能会导致捕获您不想捕获的异常。此外,构造一个 NullPointerException
对象、抛出它、捕获它、丢弃它以及创建另一个 NullPointerObject
的开销是非常重要的。
更好的是:
Integer length = null;
if (big != null) {
Small small = big.getSmall();
if (small != null) {
Tiny tiny = small.getTiny();
if (tiny != null) {
length = tiny.getTinyLength();
}
}
}
if (length != null) {
return length;
} else {
// Throw exception, return null, or do whatever
}
更好的方法是将该代码隐藏在 Big#getTinyLength()
内的 getter 中。
认真查看您的数据模型应该是有序的。你能有一个没有 Small
的 Big
吗?你能有一个不包含 Tiny
的 Small
吗?为什么 Tiny
没有长度?如果这些成员 不应该 永远是 null
,那么让 JVM 检查它并抛出异常,但不要捕获它,只是重新抛出一个等效的:
return big.getSmall().getTiny().getTinyLength();
编辑
如果您使用的是 Java 8,那么 Optional 类型可以简化您的代码:
public Integer getTinyLength (Big big) {
return Optional.ofNullable(big)
.map(Big::getSmall)
.map(Small:getTiny)
.map(Tiny::getTinyLength)
.orElse(null);
}
这从 big
构造了一个 Optional<Big>
,并继续展开它以获得微小的长度值。如果 big
是 null
,或者如果 .map(...)
中使用的任何映射函数调用 returns null
,则会生成一个空的可选值。最后,如果 Optional<Integer>
存在则返回值,否则返回 null
。
今天我正在考虑从我的代码中删除不必要的 null
条件检查,主要涉及深层嵌套对象。
例如:
我有一个 class Big
并且它引用了 Small
对象及其 getter 和 setter,同样 Small
引用了
Tiny
对象及其 getter 和 setter 以及 Tiny
对象具有 tinyLength
变量,它是一个 Integer
变量。
因此,例如,如果我正在编写一个接受 Big
对象参数并且最后 returns tinyLength
的方法,我通常会做一些事情
像这样:
public Integer getTinyLength (Big big) {
if (big!=null && big.getSmall()!=null && big.getSmall().getTiny()!=null &&
big.getSmall().getTiny().getTinyLength()!=null) {
return big.getSmall().getTiny().getTinyLength();
}
else {
// throw exception or return null or do whatever
}
所以我的问题不是做所有这些垃圾,为什么我不做类似的事情:
try {
return big.getSmall().getTiny().getTinyLength();
}
catch (Exception e){
//null pointer exception being a subclass of Exception class
log your error here
throw new NullPointerException();
}
我觉得第二个代码片段非常简洁明了。这样做有什么缺点吗? 特别是当有多个嵌套对象时,取消对每个嵌套对象的引用通常会导致代码高度不可读/如果您错过其中一个对象也容易出错,并导致空值。
第一个代码示例效率低下,并且线程不安全。 getSmall()
被调用了四次,getTiny()
被调用了三次,getTinyLength()
被调用了两次。
第二个代码示例很危险。当您只想捕获 NullPointerException
时捕获 Exception
可能会导致捕获您不想捕获的异常。此外,构造一个 NullPointerException
对象、抛出它、捕获它、丢弃它以及创建另一个 NullPointerObject
的开销是非常重要的。
更好的是:
Integer length = null;
if (big != null) {
Small small = big.getSmall();
if (small != null) {
Tiny tiny = small.getTiny();
if (tiny != null) {
length = tiny.getTinyLength();
}
}
}
if (length != null) {
return length;
} else {
// Throw exception, return null, or do whatever
}
更好的方法是将该代码隐藏在 Big#getTinyLength()
内的 getter 中。
认真查看您的数据模型应该是有序的。你能有一个没有 Small
的 Big
吗?你能有一个不包含 Tiny
的 Small
吗?为什么 Tiny
没有长度?如果这些成员 不应该 永远是 null
,那么让 JVM 检查它并抛出异常,但不要捕获它,只是重新抛出一个等效的:
return big.getSmall().getTiny().getTinyLength();
编辑
如果您使用的是 Java 8,那么 Optional 类型可以简化您的代码:
public Integer getTinyLength (Big big) {
return Optional.ofNullable(big)
.map(Big::getSmall)
.map(Small:getTiny)
.map(Tiny::getTinyLength)
.orElse(null);
}
这从 big
构造了一个 Optional<Big>
,并继续展开它以获得微小的长度值。如果 big
是 null
,或者如果 .map(...)
中使用的任何映射函数调用 returns null
,则会生成一个空的可选值。最后,如果 Optional<Integer>
存在则返回值,否则返回 null
。