Is it a bad idea if equals(null) throws NullPointerException instead?
For any non-null reference value
x ,x.equals(null) shouldreturn false .
这是很特别的,因为如果
1 2 | o1.equals(o2) // returns false o2.equals(o1) // throws NullPointerException |
事实上,
所以问题是:
- 为什么
o1.equals(o2) 应该是return false 而不是扔掉NullPointerException 是个好主意? - 如果可能的话,我们重写合同,使
anyObject.equals(null) 总是抛出NullPointerException ,这是个坏主意吗?
与
相比之下,
Note that
null is not an instance of any class, ande.compareTo(null) should throw aNullPointerException even thoughe.equals(null) returnsfalse .
如果
- 关于无效的可比较和比较契约
纯粹的语义论证
这些是
Indicates whether some other object is"equal to" this one.
什么是物体?
JLS 4.3.1对象An object is a class instance or an array.
The reference values (often just references) are pointers to these objects, and a special
null reference, which refers to no object.
从这个角度来看,我的论点很简单。
equals 测试其他对象是否"等于"this 。null 参考文献未给出试验的其他对象。- 因此,
equals(null) 应该抛出NullPointerException 。
关于这种不对称性是否不一致的问题,我认为不是,我指的是这个古老的禅k_安:
- 问任何人他是否和下一个人一样优秀,每个人都会说是的。
- 问任何人他是否和任何人一样好,每个人都会说不。
- 没人问它是否和任何人一样好,你永远不会得到答复。
在那一刻,编纂者达到了启蒙。
例外情况真的应该是例外情况。空指针可能不是程序员错误。
你引用了现有的合同。如果你决定违背惯例,在这段时间之后,当每个Java开发者都期望返回false时,你会做一些意想不到的、不受欢迎的事情,这会让你的班成为一个贱民。
我再也不能不同意了。我不会重写等于一直抛出一个异常。如果我是它的客户,我会替换任何这样做的类。
想想.equals与==和.compareTo是如何关联的,compareTo与比较运算符>、<、>=、<=。
如果你要争论使用.equals将一个对象与空值进行比较会引发一个NPE,那么你必须说这段代码也会引发一个NPE:
o1.equals(o2)和o2.equals(o1)的区别在于,在第一种情况下,您将某些内容与空进行比较,类似于o1==o2,而在第二种情况下,实际上从未执行equals方法,因此根本没有进行比较。
关于.compareto约定,将非空对象与空对象进行比较就像尝试执行以下操作:
1 2 3 4 | int j = 0; if(j > null) { ... } |
显然,这不会编译。您可以使用自动拆箱进行编译,但进行比较时会得到一个NPE,这与.compareto约定一致:
1 2 3 4 5 |
如果考虑到面向对象的概念,并考虑到整个发送者和接收者角色,那么我认为行为是方便的。在第一种情况下,你问的是一个物体,他是否等于任何人。他应该说"不,我不是"。
但在第二种情况下,你没有提到任何人,所以你没有真正问任何人。这应该引发一个例外,第一个案例不应该。
我认为只有当你忘记了对象的方向,把表达式当作数学等式时,它才是不对称的。然而,在这种模式中,双方都扮演着不同的角色,所以可以预期,秩序是重要的。
最后一点。当代码中存在错误时,应引发空指针异常。但是,询问一个对象是否是一个无名小卒,不应被视为编程缺陷。我想问问一个物体是否为空是完全可以的。如果您不控制为您提供对象的源代码怎么办?这个消息源向您发送了空值。您会检查对象是否为空,然后才查看它们是否相等吗?仅仅比较两个对象会更直观吗?无论第二个对象是什么,都会毫无例外地进行比较?
老实说,如果其主体中的equals方法故意返回空指针异常,我会很生气。等号是用来对付任何一种物体的,所以它不应该对它接收到的东西那么挑剔。如果一个equals方法返回了npe,我想最后一件事就是它是故意这样做的。特别考虑到这是一个未经检查的例外。如果你真的提升了一个NPE,那么一个人在调用你的方法之前必须记住总是检查空值,或者更糟的是,将对equals的调用包围在一个try/catch块中(上帝我讨厌try/catch块),但是哦,好吧……
不是说这是对你问题的一个必要的回答,而是一个例子,当我发现这是有用的,行为是现在的样子。
1 2 |
就目前情况而言,我能做到。
1 2 3 | if (CONSTANT_STRING.equals(text)) { // do something. } |
我没有机会得到空指针异常。如果按照您的建议进行了更改,我将不得不重新开始:
1 2 3 | if (text != null && text.equals(CONSTANT_STRING)) { // do something. } |
这是一个很好的理由使行为保持原样吗?我不知道,但这是一个有用的副作用。
在许多常见情况下,
至于为什么EDCOX1与4是不同的,在Java中调用空引用的任何实例方法都是一个编程错误,因此值得一个例外。应选择
同时,如果这两个对象都是未知的"空",那么其他代码几乎肯定需要检查其中的至少一个对象,或者在不冒
由于这是规定的方式,因此违背合同并对
它可能是以不同的方式指定的(即,
在第一种情况下,
抛开
就我个人而言,我更希望它能像它那样发挥作用。
如果按照你的建议使用
请注意,本合同为"任何非空参考X"。因此,实现过程如下:
1 2 3 4 5 | if (x != null) { if (x.equals(null)) { return false; } } |
1 2 3 4 5 6 7 8 | public boolean equals(Object obj) { // ... // If someMember is 0 this object is considered as equal to null. if (this.someMember == 0 and obj == null) { return true; } return false; } |
我认为这是为了方便和更重要的一致性——允许空值作为比较的一部分,避免了每次调用
使用实例方法进行相等、比较等,必然会使排列不对称——这对于多态性的巨大增益来说有点麻烦。当我不需要多态性时,我有时会创建一个带有两个参数的对称静态方法,
这是一个棘手的问题。对于向后兼容,您不能这样做。
想象一下下面的场景
1 2 3 4 5 |
现在,使用equals返回false else子句将被执行,但在引发异常时不会执行。
同样,空值并不等于说"2",所以返回false是完全有意义的。那么,最好坚持null.equals("b")返回同样为false:)
但这一要求确实产生了一个奇怪的非对称的等量关系。