如果 equals(null) 抛出 NullPointerException 是不是一个坏主意?
的契约equals
对于null
,如下:
对于任何非 null 引用值
x
,x.equals(null)
应返回 false
。
这是相当奇怪的,因为如果 o1 != null
且 o2 == null
,那么我们有:
o1.equals(o2) // returns false
o2.equals(o1) // throws NullPointerException
o2.equals(o1) 抛出 NullPointerException< /code> 是一件好事,因为它可以提醒我们程序员的错误。然而,如果由于各种原因我们只是将其切换到 o1.equals(o2)
,则不会捕获该错误,这只会“默默地失败”。
所以问题是:
- 为什么
o1.equals(o2)
应该返回 false
而不是抛出NullPointerException
是个好主意? - 如果我们尽可能重写合约,以便
anyObject.equals(null)
始终抛出NullPointerException
,这会是一个坏主意吗?
与 Comparable
相比,这就是 可比较
合同说:
请注意,
null
不是任何类的实例,并且e.compareTo(null)
应该抛出NullPointerException
,即使>e.equals(null)
返回false
。
如果 NullPointerException
适用于 compareTo
,为什么它不适用于 equals
?
问题
相关 语义参数
这些是 Object.equals(Object obj)
文档:
指示某个其他对象是否“等于”此对象。
什么是对象?
JLS 4.3.1 对象
对象是类实例或数组。
引用值(通常只是引用)是指向这些对象的指针,以及一个特殊的
null
引用,不引用任何对象。
从这个角度来说我的论证其实很简单。
equals
测试某个其他对象是否“等于”this
null
引用不给出其他对象< /em> 用于测试- 因此,
equals(null)
应该抛出NullPointerException
The contract of equals
with regards to null
, is as follows:
For any non-null reference value
x
,x.equals(null)
shouldreturn false
.
This is rather peculiar, because if o1 != null
and o2 == null
, then we have:
o1.equals(o2) // returns false
o2.equals(o1) // throws NullPointerException
The fact that o2.equals(o1) throws NullPointerException
is a good thing, because it alerts us of programmer error. And yet, that error would not be catched if for various reasons we just switched it around to o1.equals(o2)
, which would just "silently fail" instead.
So the questions are:
- Why is it a good idea that
o1.equals(o2)
shouldreturn false
instead of throwingNullPointerException
? - Would it be a bad idea if wherever possible we rewrite the contract so that
anyObject.equals(null)
always throwNullPointerException
instead?
On comparison with Comparable
In contrast, this is what the Comparable
contract says:
Note that
null
is not an instance of any class, ande.compareTo(null)
should throw aNullPointerException
even thoughe.equals(null)
returnsfalse
.
If NullPointerException
is appropriate for compareTo
, why isn't it for equals
?
Related questions
A purely semantical argument
These are the actual words in the Object.equals(Object obj)
documentation:
Indicates whether some other object is "equal to" this one.
And what is an object?
JLS 4.3.1 Objects
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.
My argument from this angle is really simple.
equals
tests whether some other object is "equal to"this
null
reference gives no other object for the test- Therefore,
equals(null)
should throwNullPointerException
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(12)
至于这种不对称性是否不一致的问题,我认为不是,我向您推荐这句古老的禅宗公案:
在那一刻,编译器顿悟了。
To the question of whether this asymmetry is inconsistent, I think not, and I refer you to this ancient Zen kōan:
At that moment, the compiler reached enlightenment.
异常确实应该是异常情况。空指针可能不是程序员错误。
您引用了现有合同。如果您决定违反惯例,那么经过这么长时间,当每个 Java 开发人员都希望 equals 返回 false 时,您将做一些意想不到的和不受欢迎的事情,这将使您的类成为贱民。
我完全不同意。我不会重写 equals 来一直抛出异常。如果我是它的客户,我会替换任何这样做的类。
An exception really should be an exceptional situation. A null pointer might not be a programmer error.
You quoted the existing contract. If you decide to go against convention, after all this time, when every Java developer expects equals to return false, you'll be doing something unexpected and unwelcome that will make your class a pariah.
I could't disagree more. I would not rewrite equals to throw an exception all the time. I'd replace any class that did that if I were its client.
想一想 .equals 与 == 的关系,.compareTo 与比较运算符 >、<、>=、<= 的关系。
如果您认为使用 .equals 将对象与 null 进行比较应该抛出 NPE,那么您必须说这段代码也应该抛出一个:
o1.equals(o2) 和 o2 之间的差异。 equals(o1) 是,在第一种情况下,您将某些内容与 null 进行比较,类似于 o1 == o2,而在第二种情况下,equals 方法从未实际执行,因此不会发生比较根本不。
关于 .compareTo 契约,将非空对象与空对象进行比较就像尝试这样做:
显然这不会编译。你可以使用自动拆箱来使其编译,但是当你进行比较时你会得到一个 NPE,这与 .compareTo 合约一致:
Think of how .equals is related to == and .compareTo is related to the comparison operators >, <, >=, <=.
If you're going to argue that using .equals to compare an object to null should throw a NPE, then you'd have to say that this code should throw one as well:
The difference between o1.equals(o2) and o2.equals(o1) is that in the first case you're comparing something to null, similar to o1 == o2, while in the second case, the equals method is never actually executed so there's no comparison happening at all.
Regarding the .compareTo contract, comparing a non-null object with a null object is like trying do this:
Obviously this won't compile. You can use auto-unboxing to make it compile, but you get a NPE when you do the comparison, which is consistent with the .compareTo contract:
并不是说这一定是您问题的答案,这只是我发现有用的一个例子,现在的行为是这样的。
就目前情况而言,我可以做到。
而且我没有机会得到 NullPointerException。如果按照您的建议进行更改,我将不得不重新做:
这是行为如此的充分理由吗?我不知道,但这是一个有用的副作用。
Not that this is neccessarily an answer to your question, it is just an example of when I find it useful that the behaviour is how it is now.
As it stands I can do.
And I have no chance of getting a NullPointerException. If it were changed as you suggested, I would be back to having to do:
Is this a good enough reason for the behaviour to be as it is?? I don't know, but it is a useful side-effect.
如果您考虑面向对象的概念,并考虑整个发送者和接收者角色,我会说这种行为很方便。在第一种情况下,您询问一个对象是否等于任何人。他应该说“不,我不是”。
但在第二种情况下,你没有参考任何人,所以你并没有真正询问任何人。这应该抛出异常,第一种情况不应该抛出异常。
我认为只有当您忘记面向对象并将表达式视为数学等式时,它才是不对称的。然而,在这个范式中,两端扮演着不同的角色,因此可以预期顺序很重要。
最后一点。当代码中存在错误时,应引发空指针异常。然而,询问一个对象是否无人,不应被视为编程缺陷。我认为询问一个对象是否不为空是完全可以的。如果您不控制为您提供该对象的源怎么办?并且该来源向您发送 null。您会检查对象是否为空,然后再查看它们是否相等吗?只比较两者,无论第二个对象是什么,都会毫无例外地进行比较,这不是更直观吗?
老实说,如果其体内的 equals 方法故意返回空指针异常,我会很生气。 Equals 旨在用于任何类型的对象,因此它不应该对其接收的内容如此挑剔。如果 equals 方法返回 npe,我最不想看到的就是它是故意这样做的。特别考虑到这是一个未经检查的异常。如果你确实提出了一个 npe ,那么一个人必须记住在调用你的方法之前总是检查 null ,或者更糟糕的是,在 try/catch 块中包围对 equals 的调用(上帝我讨厌 try/catch 块)但是哦,好吧。 ..
If you take object oriented concepts into account, and consider the whole sender and receiver roles, I'd say that behaviour is convenient. See in the first case you're asking an object if he is equal to nobody. He SHOULD say "NO, I'm not".
In the second case though, you don't have a reference to anyone So you aren't really asking anyone. THIS should throw an exception, the first case shouldn't.
I think it's only asymmetric if you kind of forget about object orientation and treat the expression as a mathematical equality. However, in this paradigm both ends play different roles, so it is to be expected that order matters.
As one final point. A null pointer exception should be raised when there's an error in your code. However, Asking an object if he is nobody, shouldn't be considered a programming flaw. I think it's perfectly ok to ask an object if he isn't null. What if you don't control the source that provides you with the object? and this source sends you null. Would you check if the object is null and only afterwards see if they are equals? Wouldn't it be more intuitive to just compare the two and whatever the second object is the comparison will be carried out without exceptions?
In all honesty, I would be pissed if an equals method within its body returns a null pointer exception on purpose. Equals is meant to be used against any sort of object, so it shouldn't be so picky on what it receives. If an equals method returned npe, the last thing on my mind would be that it did that on purpose. Specially considering it's an unchecked exception. IF you did raise an npe a guy would have to remember to always check for null before calling your method, or even worse, surround the call to equals in a try/catch block (God I hate try/catch blocks) But oh well...
就我个人而言,我宁愿它像现在这样执行。
NullPointerException
表明问题出在正在执行 equals 操作的对象中。的(有点无意义的)操作
如果按照您的建议使用了 NullPointerException,并且您尝试了...
o1.equals(o1)
其中 o1= null ...抛出 NullPointerException 是因为您的比较函数被搞砸了,还是因为 o1 为 null 但您没有意识到?
我知道这是一个极端的例子,但根据目前的行为,我觉得你可以很容易地看出问题出在哪里。
Personally, I'd rather it perform as it does.
The
NullPointerException
identifies that the problem is in the object against which the equals operation is being performed.If the
NullPointerException
was used as you suggest and you tried the (sort of pointless) operation of...o1.equals(o1)
where o1= null...Is the
NullPointerException
thrown because your comparison function is screwed or because o1 is null but you didn't realise?An extreme example, I know, but with current behaviour I feel you can tell easily where the problem lies.
在第一种情况下,
o1.equals(o2)
返回 false,因为o1
不等于o2
,这完全没问题。在第二种情况下,它会抛出NullPointerException
,因为o2
为null
。不能对null
调用任何方法。一般来说,这可能是编程语言的限制,但我们必须忍受它。抛出 NullPointerException 也不是一个好主意,因为您违反了 equals 方法的约定,并使事情变得比应有的更加复杂。
In the first case
o1.equals(o2)
returns false becauseo1
is not equal too2
, which is perfectly fine. In the second case, it throwsNullPointerException
becauseo2
isnull
. One cannot call any method on anull
. It may be a limitation of programming languages in general, but we have to live with it.It is also not a good idea to throw
NullPointerException
you are violating the contract for theequals
method and making things more complex than it has to be.在许多常见情况下,
null
无论如何都不例外,例如,它可能只是表示键没有值的(非例外)情况,或者代表“无”。因此,使用未知的y
执行x.equals(y)
也很常见,并且必须首先检查null
只是浪费精力。至于为什么
null.equals(y)
不同,在空引用上调用任何实例方法是一个编程错误在 Java 中,因此值得例外。应选择x.equals(y)
中x
和y
的顺序,以便知道x
不为null
。我认为,在几乎所有情况下,这种重新排序都可以根据事先了解的对象来完成(例如,从它们的起源,或者通过检查null
是否有其他方法调用)。同时,如果两个对象都具有未知的“空性”,那么其他代码几乎肯定需要检查至少其中一个,或者在不冒 NullPointerException 风险的情况下对该对象做不了太多事情。
由于这是指定的方式,因此违反约定并为
equals
的null
参数引发异常是一个编程错误。如果您考虑要求抛出异常的替代方案,那么equals
的每个实现都必须对其进行特殊处理,并且每次对equals
的调用都带有任何在调用之前必须检查潜在的null
对象。它可以以不同的方式指定(即
equals
的前提条件要求参数非null
),所以这不是为了说你的论证无效,但当前的规范使得编程语言更简单、更实用。There are many common situations where
null
is not in any way exceptional, e.g. it may simply represent the (non-exceptional) case where a key has no value, or otherwise stand for “nothing”. Hence, doingx.equals(y)
with an unknowny
is also quite common, and having to always check fornull
first would be just wasted effort.As for why
null.equals(y)
is different, it is a programming error to call any instance method on a null reference in Java, and therefore worthy of an exception. The ordering ofx
andy
inx.equals(y)
should be chosen such thatx
is known to not benull
. I would argue that in almost all cases this reordering can be done based on what is known about the objects beforehand (e.g., from their origin, or by checking againstnull
for other method calls).Meanwhile if both objects are of unknown “nullness”, then other code almost certainly requires checking at least one of them, or not much can be done with the object without risking the
NullPointerException
.And since this is the way it is specified, it is a programming error to break the contract and raise an exception for a
null
argument toequals
. And if you consider the alternative of requiring an exception to be thrown, then every implementation ofequals
would have to make a special case of it, and every call toequals
with any potentiallynull
object would have to check before calling.It could have been specified differently (i.e., the precondition of
equals
would require the argument to be non-null
), so this is not to say that your argumentation is invalid, but the current specification makes for a simpler and more practical programming language.请注意,该契约是“对于任何非空引用 x”。因此,实现将如下所示:
x
不必为null
即可被视为等于null
,因为equals 是可能的:
Note that the contract is "for any non-null reference x". So the implementation will look like:
x
need not benull
to be deemed equal tonull
because the following definition ofequals
is possible:我认为这是为了方便,更重要的是一致性 - 允许空值成为比较的一部分,可以避免每次调用
equals
时都必须进行null
检查并实现其语义。null
引用在许多集合类型中都是合法的,因此它们可以出现在比较的右侧是有意义的。使用实例方法进行相等、比较等,必然会使排列不对称——这对于多态性的巨大收益来说有点麻烦。当我不需要多态性时,有时我会创建一个带有两个参数的对称静态方法,
MyObject.equals(MyObjecta, MyObject b)
。然后,此方法检查一个或两个参数是否为空引用。如果我特别想排除空引用,那么我会创建一个附加方法,例如equalsStrict()
或类似方法,在委托给其他方法之前执行空检查。I think it's about convenience and more importantly consistency - allowing nulls to be part of the comparison avoids having to do a
null
check and implement the semantics of that each timeequals
is called.null
references are legal in many collection types, so it makes sense they can appear as the right side of the comparison.Using instance methods for equality, comparison etc., necessarily makes the arrangement asymmetric - a little hassle for the huge gain of polymorphism. When I don't need polymorphism, I sometimes create a symmetric static method with two arguments,
MyObject.equals(MyObjecta, MyObject b)
. This method then checks whether one or both arguments are null references. If I specifically want to exclude null references, then I create an additional method e.g.equalsStrict()
or similar, that does a null check before delegating to the other method.如果参数为
null
,则应返回false
。要表明这是标准,请参阅
java.util
中的“Objects.equals(Object, Object)
”,它仅对第一个参数执行不对称 null 检查(在将调用equals(Object)
)。来自 OpenJDK SE 11 源代码(SE 1.7 包含完全相同):这也将两个
null
值视为相等。You should return
false
if the parameter isnull
.To show that this is the standard, see '
Objects.equals(Object, Object)
fromjava.util
, that performs an assymetric null check on the first parameter only (on whichequals(Object)
will be called). From the OpenJDK SE 11 source code (SE 1.7 contains exactly the same):This also handles two
null
values as equal.这是一个棘手的问题。为了向后兼容,你不能这样做。
想象一下以下场景,
现在 equals 返回 false else 子句将被执行,但在抛出异常时不会被执行。
而且 null 并不真正等于“2”,因此返回 false 是非常有意义的。那么最好坚持 null.equals("b") 也返回 false :))
但这个要求确实产生了一个奇怪的、非对称的等于关系。
This is a tricky question. For backward compatability you can't do so.
Imagine the following scenario
Now with equals returning false else clause will get executed, but not when throwing an exception.
Also null is not really equal to say "2" so it makes perfect sense to return false. Then it is probably better to insist null.equals("b") to return also false :))
But this requirement does make a strange and non symmetric equals relation.