系统错误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的介绍和整个对象思想由大卫-韦斯特的书。
在这里,我将尝试总结所有的参数,并举例说明如何避免使用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
重构,以便其方法get()
在未找到任何内容的情况下将引发异常,则我们的代码将如下所示:
if (!employees.containsKey("Jeffrey")) { // first search
throw 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() {
@Override
public String name() {
return "anonymous";
}
@Override
public 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
您的代码!
相关文章
您可能还会发现以下有趣的帖子:
翻译自: https://www.javacodegeeks.com/2014/09/why-null-is-bad.html
系统错误null是什么意思