为什么NULL是错误的?
Java中NULL
用法的简单示例:
public Employee getByName(String name) {int id = database.find(name);if (id == 0) {return null;}return new Employee(id);
}
这种方法有什么问题?
它可能返回NULL
而不是对象-这是错误的。 在面向对象的范例中, NULL
是一种可怕的做法,应不惜一切代价避免使用NULL
。 关于此出版物已经有很多意见,包括“ 空引用”, Tony Hoare撰写的《十亿美元的错误》演讲和David West撰写的整本《 Object Thinking 》。
在这里,我将尝试总结所有参数,并举例说明如何避免使用NULL
并使用适当的面向对象的结构代替它们。
基本上,可以使用NULL
两种替代方法。
第一个是Null Object设计模式(最好的方法是使其成为常数):
public Employee getByName(String name) {int id = database.find(name);if (id == 0) {return Employee.NOBODY;}return Employee(id);
}
第二种可能的替代方法是在无法返回对象时抛出异常 ,从而快速失败 :
public Employee getByName(String name) {int id = database.find(name);if (id == 0) {throw new EmployeeNotFoundException(name);}return Employee(id);
}
现在,让我们看看反对NULL
的参数。
除了上面提到的Tony Hoare的演示文稿和David West的书以外,我在写这篇文章之前还阅读了这些出版物:Robert Martin的Clean Code ,Steve McConnell的Code Complete ,John Sonmez的“ No”到“ Null” , 是否返回无效的不良设计? StackOverflow上的讨论。
临时错误处理
每次获取对象作为输入时,都必须检查它是否为NULL
或有效的对象引用。 如果忘记检查,则NullPointerException
(NPE)可能会在运行时中断执行。 因此,您的逻辑将受到多次检查以及if / then / else分支的污染:
// this is a terrible design, don't reuse
Employee employee = dept.getByName("Jeffrey");
if (employee == null) {System.out.println("can't find an employee");System.exit(-1);
} else {employee.transferTo(dept2);
}
这就是应该用C和其他命令式程序语言处理异常情况的方式。 OOP引入了异常处理,主要是为了摆脱这些临时的错误处理块。 在OOP中,我们让异常冒泡直到它们到达应用程序范围的错误处理程序,并且我们的代码变得更加简洁明了:
dept.getByName("Jeffrey").transferTo(dept2);
考虑NULL
引用是过程编程的继承,请改用1)Null对象或2)异常。
模棱两可的语义
为了明确传达其含义,必须将函数getByName()
命名为getByNameOrNullIfNotFound()
。 每个返回对象或NULL
函数都应该发生同样的情况。 否则,对于代码阅读器来说,模棱两可是不可避免的。 因此,为了保持语义的明确性,应为函数指定更长的名称。
要摆脱这种歧义,请始终返回真实对象,空对象或引发异常。
有人可能会争辩说,为了性能起见,我们有时必须返回NULL
。 例如,当地图中没有这样的项目时,Java中接口Map
get()
方法将返回NULL
:
Employee employee = employees.get("Jeffrey");
if (employee == null) {throw new EmployeeNotFoundException();
}
return employee;
由于Map
使用NULL
,因此此代码仅搜索一次Map
。 如果我们将Map
重构,以便其方法get()
在未找到任何内容的情况下将引发异常,则我们的代码将如下所示:
if (!employees.containsKey("Jeffrey")) { // first searchthrow new EmployeeNotFoundException();
}
return employees.get("Jeffrey"); // second search
显然,此方法的速度是第一个方法的两倍。 该怎么办?
Map
界面(对其作者没有冒犯)具有设计缺陷。 它的方法get()
应该一直返回一个Iterator
以便我们的代码如下所示:
Iterator found = Map.search("Jeffrey");
if (!found.hasNext()) {throw new EmployeeNotFoundException();
}
return found.next();
顺便说一句,这正是C ++ STL map :: find()方法的设计方式。
计算机思维与对象思维
有人知道Java中的对象是指向数据结构的指针,而NULL
是指向0x00000000
的指针(在Intel x86处理器中为0x00000000
if (employee == null)
可以理解if (employee == null)
语句。
但是,如果您开始以对象为对象进行思考,那么这种说法就没有意义了。 这是从对象角度看我们的代码的样子:
- Hello, is it a software department?
- Yes.
- Let me talk to your employee "Jeffrey" please.
- Hold the line please...
- Hello.
- Are you NULL?
对话中的最后一个问题听起来很奇怪,不是吗?
相反,如果他们在我们请求与Jeffrey通话后挂断电话,这会给我们造成麻烦(异常)。 此时,我们尝试再次致电或通知主管,我们无法联系Jeffrey并完成更大的交易。
或者,他们可以让我们与不是Jeffrey的其他人交谈,但是可以帮助我们解决大多数问题,或者在我们需要“特定于Jeffrey”的东西时拒绝帮助(空对象)。
缓慢失败
上面的代码没有快速失败 ,而是尝试缓慢消失,从而杀死了其他人。 它没有让所有人都知道出了什么问题并且应该立即开始异常处理,而是向客户端隐藏了此故障。
该参数与上面讨论的“临时错误处理”非常接近。
最好使代码尽可能脆弱,并在必要时让代码中断。
使您的方法对它们操作的数据极为苛刻。 如果提供的数据不足或根本不适合该方法的主要使用场景,请让他们通过引发异常来进行抱怨。
否则,返回一个Null对象,该对象暴露一些常见行为,并在所有其他调用上引发异常:
public Employee getByName(String name) {int id = database.find(name);Employee employee;if (id == 0) {employee = new Employee() {@Overridepublic String name() {return "anonymous";}@Overridepublic void transferTo(Department dept) {throw new AnonymousEmployeeException("I can't be transferred, I'm anonymous");}};} else {employee = Employee(id);}return employee;
}
可变和不完整的对象
通常, 强烈建议设计时牢记不变性的对象。 这意味着对象在实例化过程中会获得所有必需的知识,并且在整个生命周期中都不会改变其状态。
通常,在延迟加载中使用NULL
值,以使对象不完整且可变。 例如:
public class Department {private Employee found = null;public synchronized Employee manager() {if (this.found == null) {this.found = new Employee("Jeffrey");}return this.found;}
}
该技术尽管被广泛使用,但却是OOP中的反模式。 主要是因为它使对象负责计算平台的性能问题,而这是Employee
对象不应该意识到的。
对象不必管理状态并公开其与业务相关的行为,而必须处理其自身结果的缓存-这就是延迟加载的意义所在。
缓存不是员工在办公室里做的事情,对吗?
解决方案? 请勿以上述原始方式使用延迟加载。 相反,请将此缓存问题移至应用程序的另一层。
例如,在Java中,您可以使用面向方面的编程方面。 例如, jcabi-aspects具有@Cacheable
批注,用于缓存方法返回的值:
import com.jcabi.aspects.Cacheable;
public class Department {@Cacheable(forever = true)public Employee manager() {return new Employee("Jacky Brown");}
}
我希望这种分析令人信服,您将停止NULL
您的代码!
相关文章
您可能还会发现以下有趣的帖子:
- Java代码中的典型错误
- 实用程序类的OOP替代
- 避免字符串串联
- 对象应该是不可变的
翻译自: https://www.javacodegeeks.com/2014/09/why-null-is-bad.html
为什么NULL是错误的?相关推荐
- 关于出现org.hibernate.TransientObjectException: The given object has a null identifier: 错误的解决方法
关于出现org.hibernate.TransientObjectException: The given object has a null identifier: 错误的解决方法 参考文章: (1 ...
- golang 数据库null值错误 解决方法
遇到问题: converting NULL to string is unsupported 定义的结构体中 结构体成员类型为string,从mysql数据库中查询数据中有NULL值,go语言执行sc ...
- 系统错误null是什么意思_为什么NULL是错误的?
系统错误null是什么意思 Java中NULL用法的简单示例: public Employee getByName(String name) {int id = database.find(name) ...
- Can not set int field to null value错误解决:
查询数据库记录报错: 错误信息: Can not set int field /*某个类的属性*/ to null value 不能设置int类型的属性为null值 原因就是:数据库中有类型为int的 ...
- Exception occurred during processing request: null 的错误原因以及解决办法
欢迎大家关注我的公众号,添加我为好友! 如果用的是ssh框运行的时候遇到了"Exception occurred during processing request: null"错 ...
- golang 读取 mysql null 字符串错误
例子: var query = `SELECT buin, gid, firstMsgId, lastReadMsgId,maxMsgId, handle, state FROM XX rows,er ...
- java.lang.NullPointerException: null的错误
在调试后台程序时,出现了java.lang.NullPointerException: null的问题,控制台打印的日志如下: 2022-02-14 06:55:21.313 ERROR 11600 ...
- 'NULL' undeclared错误
在dev_c++中需要包含stdio.h 其他编译器可能要using namespace std 等
- shell /dev/null
/dev/null表示空设备,这里就是把日志记录到空设备里,就是不记录日志,Null 是一特殊指标值(或是一种物件参照 reference)表示这个指标并不指向任何的物件. 是一个特殊的文件,写入到它 ...
最新文章
- 阿里用回mysql_回mysql方式
- Java实现ANN神经网络之BP代码参考
- web_find和web_reg_find的用法和区别
- 学计算机后悔转专业,大学转专业容易吗 会后悔吗
- 企业如何快速应对市场环境的不断变化
- Asp.NET的DESAES加密算法(转载)
- web提升服务器性能,开启一个参数就能让你的WEB性能提升3倍
- 王思聪、贾跃亭、罗永浩组成神奇的三角债?罗永浩回应...
- gis环境设置在哪_BIM与GIS如何融合应用?
- 用计算机控制神舟十号,整理后的计算机控制系统复习题答案
- CAD2020软件安装教程【搬运】
- 2015.3.12Arinc424 Tools中SiniArincCls.csParserFile(string sFile)函数正则表达式理解
- 干货:我的web3研究框架分享
- 遇到mysqladmin flush-hosts报错解决思路
- Linux:共享内存
- TNF抑制剂相关的肿瘤风险:阿达木单抗、依那西普和英夫利昔单抗随机对照试验的荟萃分析...
- mysql靶场_搭建sqli靶场
- 戴尔服务器改win7系统,戴尔电脑怎么把Win10系统改装win7系统?
- 软件测试十七阶段(测试电商项目)
- 非线性系统【二】Lyapunov稳定性
热门文章
- 把本地文件上传到gitee
- 2013蓝桥杯-B-省赛-六-三部排序
- 虚拟机安装centeros7 无法连接网络 virsh命令找不到 删除多余的vir0 不然dubbo会有问题
- 范式 第一 第二 第三范式
- 转:聊聊开发中幂等性问题(*)
- thinking-in-java(13) String字符串
- 转移指令jmp和跳转指令call
- restful rest_HATEOAS的RESTful服务。 REST:刷新器
- swarm 和 k8s_Wildfly Swarm,朝着成熟和一小部分贡献
- jsf xhtml调用方法_JSF的工作方式以及调试方法–可以使用polyglot吗?