null and the symmetry of equals
平等应该是对称的,对吗?
不让第二种形式抛出一个NullPointerException的理由是什么?
- 声明null.equals(someObject)不能编译,至少我不能。你是如何编译它的?
- 如果等于(null)而引发NullPointerException,可能重复的是一个坏主意吗?
- @保罗:显然是伪代码。现在更好了吗?:)
- @弗雷德,不是真的。如果您花2秒阅读EDCOX1的API(1),您将看到"Erras方法在非空对象引用上实现等价关系"。如果您阅读了空值的定义,可能会有所帮助。我在下面的答案中链接到Java语言规范。
- @保罗:如果"equals方法在非空对象引用上实现等价关系",那么传递空值肯定会引发异常,因为空值不是非空值。
- 保罗:我没有问为什么Java的行为在API中描述。我想知道为什么API在一开始就是这样定义的。到目前为止,我还没有看到任何令人信服的论据,即在将null传递给equals时抛出NullPointerException是根本错误的。
- @弗雷德,你还没有提出一个令人信服的理由,为什么它应该抛出一个NPE。我已经解释了为什么它不会-对象的值不等于字面上的空引用。
- @保罗:争论是对称的。我希望a.equals(b)和b.equals(a)的意思相同。
- @弗雷德,我认为你对空是一个值这一想法很感兴趣。不是这样。它表示空引用:指向Nothing的指针。equals用于比较值;当与对象一起使用时,==用于比较引用。作为一个坏的类比,就像你在说"我要我的苹果剥皮机剥这个橘子"。苹果剥皮工具是为苹果,正如equals是为价值。对象和原语的值都不能为空;对象的引用不能为空。
- @保罗:看来你同意我的看法:如果equals是用来比较值的,而null不是一个值,那么将null传递给equals应该是一个错误,对吧?
- @佛瑞德,我一点也不同意,平等者抛出一个例外对程序来说是可怕的。打电话给equals()时,我只关心是非。在多年的编程中,我从未关注过第三种状态:未定义(见三值逻辑)。在调用equals之前,是否要将对equals的每个调用包装在try-catch块中,或检查是否为空?当然不是。如果你需要一个3值的等号(这就是你所提倡的…真、假、例外),那么在你调用等号或编写一个接口之前,先检查一下是否为空。
- @fred:null是一个值,尽管是一个空值(实际上是实现的最低形式的二进制0)。
- @保罗:"平等被抛出一个例外对于编程来说是可怕的。[…]是否要在try catch块中包装对equals的每个调用,或在调用equals之前检查是否为空?"但是我必须对点左边的引用进行空检查,不管怎样:)如果你能给我看一个真正的代码片段,它从等于(空)返回false而不是抛出异常中获益,我将投票并接受你的答案。
- @佛瑞德,每次我使用"等于从空中获益"-->false。当我使用equals时,我想知道2件事中的1件:它们是相等的还是不相等的。null->false满足了这一需求。我从不想知道三件事中的一件:它们是相等的、不相等的还是未定义的。在需要了解第三种状态的地方,您能提出什么情况?
在第一种情况下,equals()方法不会抛出NPE,因此您不能使用该参数。对称性是equals()方法的契约的一部分。
- 对称性只是非空值的契约的一部分。
- @保罗:没错,但问题是为什么会这样,我指出这个问题的前提是有缺陷的,因为equals()没有抛出NPE。
在理论意义上,平等当然是被定义为对称的,但在不存在的对象(即空值所代表的对象)上,它也根本没有被定义。
因此,任何应用于空值的行为都同样有效。它可以使一只活生生的兔子回归,但仍然不违背平等的理论定义。
在这种情况下,代表Java设计器的一个非常合理的实现决策,在空值上调用等值应该抛出一个Null PoExtExchange,这与调用null值上的任何其他方法是一致的。
- 您会认为异常是不同的,或者根本不会编译。
- @0A0D-我认为这只是保持一致的一个实际问题-您希望在运行时取消对空指针的引用以引发空指针异常,否则人们会对可能的原因感到困惑….
- 我能看到。这看起来很有趣,因为它明确地声明它只涉及非空引用。因为这里的空值是显式的空值,所以我们认为编译器会立即停止。该值已在运行时之前已知。
因为在第二种情况下,您没有访问null对象的方法。平等的概念不是不平衡的,而是你如何去理解它。
第二位不会抛出NPE,因为您没有取消对空指针的引用。该代码返回false,因为您正在将值与非值进行比较。
equals(null)总是返回false,因为没有空值。由于Java中没有概念,对象和基元都不能具有EDCOX1×0的值。null是一个表示空引用的文本,这就是我们比较引用的原因,例如if (obj == null)。参见Java语言规范,3.3.7节。换句话说,您将EDOCX1的值(7)与空引用进行比较。
您可以创建自己的对象,重写equals,然后返回true,但这与对象中的定义不符。
我愿意去
1
| someObject.equals(null); |
What is the rational for not having the second form throw a
NullPointerException?
在这种情况下,它不会抛出NullPointerException。
在这里,我们可以肯定,equals()将调用的对象是NOT NULL。
第二个例子不是对称的例子,因为它违反了关于对象和equals()方法的简单规则之一:
For any non-null reference value x, x.equals(null) should return
false.
- 好吧,但是为什么合同是这样定义的?我们从这种不对称中得到了什么?为什么不说"x.equals(null)"应该抛出一个NullPointerException?
- @FredOverflow:因为NullPointerException是一个运行时异常,当它遵循object.equals()约定的规则时,为某个异常抛出异常是没有意义的。
- @fred,null不是一个值,因此一个对象不能等于空值,就像您可以将null赋给原语一样。当您检查if (x == null)时,实际上是在检查指针引用的地址。
- 所以契约被定义为在与空比较时不引发异常,因为如果与空比较确实引发了异常,那么它会违反契约吗?这不是一个很有说服力的论点。我也可以说,如果合同被定义为将异常设为空,那么将该异常设为空将完全符合合同的要求。
- @FredOverflow:这对于NullPointerException没有意义,因为您没有取消对空指针的引用。
- API说:"当应用程序试图在需要对象的情况下使用空值时抛出。其中包括:…]应用程序应该抛出此类的实例,以指示对空对象的其他非法使用。"事实上,我见过许多API在传递空值时抛出NullPointerException。
- @弗雷德:这个周末给我研究。恐怕你能得到的唯一答案是因为。
- @FredOverflow:如果给定了空引用,则对对象执行操作的方法应该抛出NPE,而对引用执行操作的方法应该识别出空引用是引用,尽管它不标识对象。equals询问"此引用是否标识了与您等效的对象"。如果引用标识了一个可能等效的对象,它可能需要检查对象的详细信息以确定它是否等效,但是如果引用没有标识正确类型的对象,那么代码根本没有理由检查对象本身。