Object
Java契约的一个基本部分是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()
实现没有考虑到这一点!这意味着返回true
for 的两个 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 实现中的这个基本问题的认可和行动?