Pulpcode

捕获,搅碎,拼接,吞咽

0%

我的代码要如何处理NULL

在运行代码的时候,最烦的就是看见Null了,而程序经常遇到的错误也是NullPointException(空指针异常)。就算是写代码处理Null,也烦的要死,因为要在各处重复的写:

1
2
3
4
5
6

if (xxx == null){
...
}else{
...
}

然而很多时候你可能就忘写了。

当然还有的人,跟我一样纠结,认为代码不能写太多的废话,比如我明知道这个函数不会返回null,那我为什么要在这里判断是否为null,那这样我的代码是不是显得非常蠢啊?

但是你还是会有点犹豫,毕竟你害怕哪天,真的就空指针了,而你并没有判断。所以很多强迫症患者一直在思考,这代码怎样写才算好。

所以这篇博客。总结一下,到底应该怎样处理这个null

什么是null

在语言层面,定义null,返回null,没有任何问题,因为它确实代表着什么都没有,而且也不和0,空字符串等价。就像数据库,也是既有null值又有空类型的值(比如空字符串),它们并不能被混淆。

而在代码层面(工程层面),之所以报错,也就是所谓的NullPointException,只不过因为对一个不存在的东西进行了解引用罢了。

比如你调用了一个null对象的属性或者方法。类似a.b(),那这就自然会报空指针异常了。
再或者对一个null的容器,通过索引进行调用,也会出现空指针异常。
还有就是用for去迭代一个null容器。
以上这些都是常见的空指针异常错误的原因。

对null的处理

大部分对null的处理,就是在解引用前,先判断是否为null。

其实说白了,对null的处理,其实就是在做这样一种判断:

if(上下文正确){
xxxxxx
}
而上下文不正确,可能会做一些其他的事情,也可能什么也不做。

不仅仅是null,在编程的其它场景,在做某事之前,先检查上下文是否合理,也是一个好的编程习惯,《代码大全》中所提出的防御式编程,就是这个思路,你所有函数在一开始先判断传入的参数是否是合法的。

其它一些尝试

我认为null总是和try .. catch相伴随,try … catch 用来处理那些不确定的元素,比如读文件,比如调用三方接口,这些地方都有可能返回null。而作为你的确定代码,当返回一个null时,就要从代码逻辑上考虑,这里返回一个null是否应该。

容器

比如对于容器类,很多开发都建议,如果什么结果都没有得到,那也不要返回null,最好返回一个空容器。这样你的代码如果是迭代这个容器并做一些操作,那么空容器,只不过什么都不用干了,而你也不会每次都要苦恼的写null。

1
2
3
if(collection is not empty){
....
}

Null对象

有些设计会尝试用Null对象来替代Null, 而这个所谓的Null对象,就是包含正常对象的所有接口,只不过这些方法的实现都是空的,也就是说什么也不做。

比如你根据需要创建了许多的Task,然后在一个一个的在不同的execute函数中,改变它们的状态机。而如果你创建的task对象有可能是null,那就要保证,你的每个方法都要在代码的一开始,判断是否为null,所以一个好的解决办法就是做一个NullTask,它提供所有Task所提供的方法可以被你调用,只是它什么都不做罢了。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16

class Task implements IRun{
public void run(){
....xxx
....xxx
}
}

class NullTask implements IRun{
public void run(){}
}

for (Task task: tasks){
task.run()
}

很多为了防止大量写 if null ,之类的判断逻辑时,都采用这种设计。但是它好用的前提是,你的上下文语义是:“如果是null,我就什么都不做。”所以这种设计,在循环操作中非常实用。

标记位

if null的写法,让你会常常忘记先判断是否为null

python的代码有一种写法,就是用来将“函数动作本身”和“函数结果本身”,相分离。

1
2
3
4
5
ok, result = fetch_something()
if ok :
result
xxxxx
xxxxx

不仅仅是将动作和结果本身相分离,也让你强行先用ok判断一下是否为null。

这种标记位在接口设计的时候也非常实用,比如你为函数添加了一个新逻辑,或者为接口添加了一个新字段,那最好配置一个标记位,这样代码写起来,就是

1
2
3
if(flag){
从目标字段获取数据
}

Optional

java8之后,添加的Optional,包装null的全套处理,让你统一的处理判断。

也有一种让你强行先判断是否为null的感觉。

1
2
3
4
5
Optional maybeempty = Optional.ofNullable(maybenull);
if (maybeempty.isPresent()){
maybeempty.get();
.....
}

当然除了isPresent,还提供:orElse之类的写法。python中的字典有一个类似的写法:dic.get(key,default),既如果拿不到,就返回default,而不是抛异常。