12

ObjectJava契约的一个基本部分是hashCode()方法应该与equals()方法一致。这是有道理的,也很容易理解:如果两个对象在某种程度上“相等”,它们应该返回相同的哈希码。如果没有,例如,您可以将一个对象放入 aHashSet中,然后稍后检查是否有单独的实例在集合中并错误地返回false,即使该equals()方法会认为这些对象是等效的。

事实上,Java 的 URI 代码从 Java 6 开始就有这个问题。试试这个代码:

import static org.hamcrest.CoreMatchers.*;
import static org.junit.Assert.*;

import java.net.URI;

import org.junit.Test;

public class URITest
{

    @Test
    public void testURIHashCode()
    {
        final URI uri1 = URI.create("http://www.example.com/foo%2Abar");
        final URI uri2 = URI.create("http://www.example.com/foo%2abar");
        assertThat("URIs are not equal.", uri1, equalTo(uri2));
        assertThat("Equal URIs do not have same hash code.", uri1.hashCode(), equalTo(uri2.hashCode()));
    }
}

根据 RFC 3968,URI 转义序列不区分大小写;也就是说,%2A并且%2a被认为是等效的。JavaURI.equals()实现考虑到了这一点。但是,URI.hashCode()实现没有考虑到这一点!这意味着返回truefor 的两个 URI 实例URI.equals()仍然可以返回不同的哈希码,如上面的代码所示!

我提交了这个问题,据说它会导致 Java 错误 7134993,但该错误不再可用。但是,Java错误 7054089中显示了相同的问题。(我不确定这是来自我的提交还是来自其他人,但问题是一样的。)但是,该错误在评估中被拒绝,“引用的示例是不透明的 URI,因此方案特定部分不是解析。”

评估此错误的人一定不熟悉它的含义equals()hashCode()保持一致。合约Object.equals()明确规定,“如果两个对象根据 equals(Object) 方法相等,则对两个对象中的每一个调用 hashCode 方法必须产生相同的整数结果。” 注意使用“必须”,而不是“应该”。

这里的重点是,即使评估者声称 URI 是“不透明的”和“未解析的”,但URI.equals()实现(与他/她的主张相反)确实是解析 URI 并考虑到不区分大小写。URI.hashCode()实施不是。

那么我在这里是否完全密集并且遗漏了一些明显的东西?如果我是,请有人告诉我我的错误,我会将您的答案标记为正确。否则,问题是:现在 Sun/Oracle 似乎不再允许对已提交的错误发表评论,我有什么办法来获得对 Internet 主要标识符 URI 的 Java 实现中的这个基本问题的认可和行动?

4

2 回答 2

4

我将使用您在此处提供的示例而不是错误 7054089 中的示例重新提交针对 Java 1.7.0_17 的错误。我检查了您的 StackOverflow 示例是否也适用于该版本。我听说 Oracle 已经关闭了 Java 6 的错误修复,除了安全问题。

在您提交的原始错误中,您提供的 URI 是不透明的 URI,这可能会导致评估器失败。我认为你的意思是 RFC 2396。

此外,您可能会得到一个新的评估者:)

在我看来,他们在这里肯定违反了 hashCode() 的合同。

遗憾的是,他们没有类似 StackOverflow 的 Java 评论机制(或他们过去拥有的基本评论)。

于 2013-04-29T05:54:14.663 回答
2

好消息!Oracle 似乎已在JDK-7171415中修复了 Java 8 的此错误!我已经验证我上面的测试现在在 Windows 10 Pro 64 位上的 Java 1.8.0_92 中通过了。新的 bug 票没有引用任何以前的,所以我不知道它是怎么发生的。但我很高兴他们终于修好了。

于 2016-09-14T15:24:55.593 回答