如果 equals(null) 抛出 NullPointerException 是不是一个坏主意?

发布于 2024-09-02 17:12:25 字数 2314 浏览 7 评论 0原文

的契约equals 对于null,如下:

对于任何非 null 引用值 xx.equals(null)返回 false

这是相当奇怪的,因为如果 o1 != nullo2 == 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) should return 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) should return false instead of throwing NullPointerException?
  • Would it be a bad idea if wherever possible we rewrite the contract so that anyObject.equals(null) always throw NullPointerException instead?

On comparison with Comparable

In contrast, this is what the Comparable contract says:

Note that null is not an instance of any class, and e.compareTo(null) should throw a NullPointerException even though e.equals(null) returns false.

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 throw NullPointerException

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(12

小兔几 2024-09-09 17:12:25

至于这种不对称性是否不一致的问题,我认为不是,我向您推荐这句古老的禅宗公案:

  • 问任何一个人是否与下一个人一样好,每个人都会说是。
  • 问任何一个人是否比任何人都优秀,每个人都会说“不”。
  • 如果问任何人是否和任何人一样好,你永远不会得到答复。

在那一刻,编译器顿悟了。

To the question of whether this asymmetry is inconsistent, I think not, and I refer you to this ancient Zen kōan:

  • Ask any man if he's as good as the next man and each will say yes.
  • Ask any man if he's as good as nobody and each will say no.
  • Ask nobody if it's as good as any man and you'll never get a reply.

At that moment, the compiler reached enlightenment.

情话墙 2024-09-09 17:12:25

异常确实应该是异常情况。空指针可能不是程序员错误。

您引用了现有合同。如果您决定违反惯例,那么经过这么长时间,当每个 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.

旧人哭 2024-09-09 17:12:25

想一想 .equals 与 == 的关系,.compareTo 与比较运算符 >、<、>=、<= 的关系。

如果您认为使用 .equals 将对象与 null 进行比较应该抛出 NPE,那么您必须说这段代码也应该抛出一个:

Object o1 = new Object();
Object o2 = null;
boolean b = (o1 == o2); // should throw NPE here!

o1.equals(o2) 和 o2 之间的差异。 equals(o1) 是,在第一种情况下,您将某些内容与 null 进行比较,类似于 o1 == o2,而在第二种情况下,equals 方法从未实际执行,因此不会发生比较根本不。

关于 .compareTo 契约,将非空对象与空对象进行比较就像尝试这样做:

int j = 0;
if(j > null) { 
   ... 
}

显然这不会编译。你可以使用自动拆箱来使其编译,但是当你进行比较时你会得到一个 NPE,这与 .compareTo 合约一致:

Integer i = null;
int j = 0;
if(j > i) { // NPE
   ... 
}

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:

Object o1 = new Object();
Object o2 = null;
boolean b = (o1 == o2); // should throw NPE here!

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:

int j = 0;
if(j > null) { 
   ... 
}

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:

Integer i = null;
int j = 0;
if(j > i) { // NPE
   ... 
}
贱贱哒 2024-09-09 17:12:25

并不是说这一定是您问题的答案,这只是我发现有用的一个例子,现在的行为是这样的。

private static final String CONSTANT_STRING = "Some value";
String text = getText();  // Whatever getText() might be, possibly returning null.

就目前情况而言,我可以做到。

if (CONSTANT_STRING.equals(text)) {
    // do something.
}

而且我没有机会得到 NullPointerException。如果按照您的建议进行更改,我将不得不重新做:

if (text != null && text.equals(CONSTANT_STRING)) {
    // do something.
}

这是行为如此的充分理由吗?我不知道,但这是一个有用的副作用。

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.

private static final String CONSTANT_STRING = "Some value";
String text = getText();  // Whatever getText() might be, possibly returning null.

As it stands I can do.

if (CONSTANT_STRING.equals(text)) {
    // do something.
}

And I have no chance of getting a NullPointerException. If it were changed as you suggested, I would be back to having to do:

if (text != null && text.equals(CONSTANT_STRING)) {
    // do something.
}

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.

只为一人 2024-09-09 17:12:25

如果您考虑面向对象的概念,并考虑整个发送者和接收者角色,我会说这种行为很方便。在第一种情况下,您询问一个对象是否等于任何人。他应该说“不,我不是”。

但在第二种情况下,你没有参考任何人,所以你并没有真正询问任何人。这应该抛出异常,第一种情况不应该抛出异常。

我认为只有当您忘记面向对象并将表达式视为数学等式时,它才是不对称的。然而,在这个范式中,两端扮演着不同的角色,因此可以预期顺序很重要。

最后一点。当代码中存在错误时,应引发空指针异常。然而,询问一个对象是否无人,不应被视为编程缺陷。我认为询问一个对象是否不为空是完全可以的。如果您不控制为您提供该对象的源怎么办?并且该来源向您发送 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...

债姬 2024-09-09 17:12:25

就我个人而言,我宁愿它像现在这样执行。

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.

£噩梦荏苒 2024-09-09 17:12:25

在第一种情况下,o1.equals(o2) 返回 false,因为 o1 不等于 o2,这完全没问题。在第二种情况下,它会抛出 NullPointerException,因为 o2null。不能对 null 调用任何方法。一般来说,这可能是编程语言的限制,但我们必须忍受它。

抛出 NullPointerException 也不是一个好主意,因为您违反了 equals 方法的约定,并使事情变得比应有的更加复杂。

In the first case o1.equals(o2) returns false because o1 is not equal to o2, which is perfectly fine. In the second case, it throws NullPointerException because o2 is null. One cannot call any method on a null. 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 the equals method and making things more complex than it has to be.

夏日落 2024-09-09 17:12:25

在许多常见情况下,null 无论如何都不例外,例如,它可能只是表示键没有值的(非例外)情况,或者代表“无”。因此,使用未知的 y 执行 x.equals(y) 也很常见,并且必须首先检查 null 只是浪费精力。

至于为什么 null.equals(y) 不同,在空引用上调用任何实例方法一个编程错误在 Java 中,因此值得例外。应选择 x.equals(y)xy 的顺序,以便知道 x不为null。我认为,在几乎所有情况下,这种重新排序都可以根据事先了解的对象来完成(例如,从它们的起源,或者通过检查 null 是否有其他方法调用)。

同时,如果两个对象都具有未知的“空性”,那么其他代码几乎肯定需要检查至少其中一个,或者在不冒 NullPointerException 风险的情况下对该对象做不了太多事情。

由于这是指定的方式,因此违反约定并为 equalsnull 参数引发异常是一个编程错误。如果您考虑要求抛出异常的替代方案,那么 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, doing x.equals(y) with an unknown y is also quite common, and having to always check for null 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 of x and y in x.equals(y) should be chosen such that x is known to not be null. 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 against null 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 to equals. And if you consider the alternative of requiring an exception to be thrown, then every implementation of equals would have to make a special case of it, and every call to equals with any potentially null 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.

城歌 2024-09-09 17:12:25

请注意,该契约是“对于任何非空引用 x”。因此,实现将如下所示:

if (x != null) {
    if (x.equals(null)) {
        return false;
    }
}

x 不必为 null 即可被视为等于 null,因为 equals 是可能的:

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;
}

Note that the contract is "for any non-null reference x". So the implementation will look like:

if (x != null) {
    if (x.equals(null)) {
        return false;
    }
}

x need not be null to be deemed equal to null because the following definition of equals is possible:

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;
}
如果没有 2024-09-09 17:12:25

我认为这是为了方便,更重要的是一致性 - 允许空值成为比较的一部分,可以避免每次调用 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 time equals 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.

皇甫轩 2024-09-09 17:12:25

如果参数为 null,则应返回 false

要表明这是标准,请参阅 java.util 中的“Objects.equals(Object, Object)”,它仅对第一个参数执行不对称 null 检查(在将调用 equals(Object))。来自 OpenJDK SE 11 源代码(SE 1.7 包含完全相同):

public static boolean equals(Object a, Object b) {
    return (a == b) || (a != null && a.equals(b));
}

这也将两个 null 值视为相等。

You should return false if the parameter is null.

To show that this is the standard, see 'Objects.equals(Object, Object) from java.util, that performs an assymetric null check on the first parameter only (on which equals(Object) will be called). From the OpenJDK SE 11 source code (SE 1.7 contains exactly the same):

public static boolean equals(Object a, Object b) {
    return (a == b) || (a != null && a.equals(b));
}

This also handles two null values as equal.

千笙结 2024-09-09 17:12:25

这是一个棘手的问题。为了向后兼容,你不能这样做。

想象一下以下场景,

void m (Object o) {
 if (one.equals (o)) {}
 else if (two.equals (o)) {}
 else {}
}

现在 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

void m (Object o) {
 if (one.equals (o)) {}
 else if (two.equals (o)) {}
 else {}
}

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.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文