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是错误的?相关推荐

  1. 关于出现org.hibernate.TransientObjectException: The given object has a null identifier: 错误的解决方法

    关于出现org.hibernate.TransientObjectException: The given object has a null identifier: 错误的解决方法 参考文章: (1 ...

  2. golang 数据库null值错误 解决方法

    遇到问题: converting NULL to string is unsupported 定义的结构体中 结构体成员类型为string,从mysql数据库中查询数据中有NULL值,go语言执行sc ...

  3. 系统错误null是什么意思_为什么NULL是错误的?

    系统错误null是什么意思 Java中NULL用法的简单示例: public Employee getByName(String name) {int id = database.find(name) ...

  4. Can not set int field to null value错误解决:

    查询数据库记录报错: 错误信息: Can not set int field /*某个类的属性*/ to null value 不能设置int类型的属性为null值 原因就是:数据库中有类型为int的 ...

  5. Exception occurred during processing request: null 的错误原因以及解决办法

    欢迎大家关注我的公众号,添加我为好友! 如果用的是ssh框运行的时候遇到了"Exception occurred during processing request: null"错 ...

  6. golang 读取 mysql null 字符串错误

    例子: var query = `SELECT buin, gid, firstMsgId, lastReadMsgId,maxMsgId, handle, state FROM XX rows,er ...

  7. java.lang.NullPointerException: null的错误

    在调试后台程序时,出现了java.lang.NullPointerException: null的问题,控制台打印的日志如下: 2022-02-14 06:55:21.313 ERROR 11600 ...

  8. 'NULL' undeclared错误

    在dev_c++中需要包含stdio.h 其他编译器可能要using namespace std 等

  9. shell /dev/null

    /dev/null表示空设备,这里就是把日志记录到空设备里,就是不记录日志,Null 是一特殊指标值(或是一种物件参照 reference)表示这个指标并不指向任何的物件. 是一个特殊的文件,写入到它 ...

最新文章

  1. 阿里用回mysql_回mysql方式
  2. Java实现ANN神经网络之BP代码参考
  3. web_find和web_reg_find的用法和区别
  4. 学计算机后悔转专业,大学转专业容易吗 会后悔吗
  5. 企业如何快速应对市场环境的不断变化
  6. Asp.NET的DESAES加密算法(转载)
  7. web提升服务器性能,开启一个参数就能让你的WEB性能提升3倍
  8. 王思聪、贾跃亭、罗永浩组成神奇的三角债?罗永浩回应...
  9. gis环境设置在哪_BIM与GIS如何融合应用?
  10. 用计算机控制神舟十号,整理后的计算机控制系统复习题答案
  11. CAD2020软件安装教程【搬运】
  12. 2015.3.12Arinc424 Tools中SiniArincCls.csParserFile(string sFile)函数正则表达式理解
  13. 干货:我的web3研究框架分享
  14. 遇到mysqladmin flush-hosts报错解决思路
  15. Linux:共享内存
  16. TNF抑制剂相关的肿瘤风险:阿达木单抗、依那西普和英夫利昔单抗随机对照试验的荟萃分析...
  17. mysql靶场_搭建sqli靶场
  18. 戴尔服务器改win7系统,戴尔电脑怎么把Win10系统改装win7系统?
  19. 软件测试十七阶段(测试电商项目)
  20. 非线性系统【二】Lyapunov稳定性

热门文章

  1. 把本地文件上传到gitee
  2. 2013蓝桥杯-B-省赛-六-三部排序
  3. 虚拟机安装centeros7 无法连接网络 virsh命令找不到 删除多余的vir0 不然dubbo会有问题
  4. 范式 第一 第二 第三范式
  5. 转:聊聊开发中幂等性问题(*)
  6. thinking-in-java(13) String字符串
  7. 转移指令jmp和跳转指令call
  8. restful rest_HATEOAS的RESTful服务。 REST:刷新器
  9. swarm 和 k8s_Wildfly Swarm,朝着成熟和一小部分贡献
  10. jsf xhtml调用方法_JSF的工作方式以及调试方法–可以使用polyglot吗?