我不明白为什么两个文件的URI
相等,但它们的String
表示不相等。这是一个错误吗?
assertEquals(new File(".").toURI(), Paths.get(".").toUri()); // pass
assertEquals(new File(".").toURI().toString(), Paths.get(".").toUri().toString()); // fail
// org.opentest4j.AssertionFailedError:
// Expected :file:/path/to/the/directory/./
// Actual :file:///path/to/the/directory/./
更新 1
我知道,从技术上讲,您可以完全实现,其中两个对象基于equals
方法相等,但具有不同的toString()
结果。
但是,我只是好奇,在类 URI
的情况下,它是否是一个很好的实现:两个URI
相等但具有不同的字符串表示形式。
就我而言,这是不同的,因为有不同的getAuthority()
结果。但是,为什么它们是平等的呢?这是令人困惑的。如果我没有打印出结果并检查它的源代码,我就不会意识到这一点。
更新 2
根据@VGR下面的评论,我做了另一个测试如下:
System.out.println(new File(".").toURI()); // file:/path/to/the/directory/./
System.out.println(Paths.get(".").toUri()); // file:///path/to/the/directory/./
System.out.println(new File(".").toURI().getAuthority()); // null
System.out.println(Paths.get(".").toUri().getAuthority()); // null
如您所见,如果我们从这两个URI
中得到authority
,则在这两种情况下都是null
的。
但是,它们没有相同的toString()
输出。
URI#toString()
方法在这里有很好的文档:
以字符串形式返回此 URI 的内容。 如果此 URI 是通过调用此类中的一个构造函数创建的,则返回与原始输入字符串等效的字符串,或与从原始给定组件计算的字符串(视情况而定(等效。否则,此 URI 是通过规范化、解析或相对化创建的,因此将根据 RFC 2396 第 5.2 节步骤 7 中指定的规则从此 URI 的组件构造字符串。
这意味着,toString()
方法的结果取决于我们初始化 URI 对象的方式(总共 5 个构造函数(。
在本例中,您尝试以 2 种不同的方式初始化 2 个 URI 对象,以便toString()
的结果不同。可以尝试normalize()
方法。
没有这样的契约,如果两个对象相等,字符串表示需要相同。Java equals
实现独立于toString
方法。例如,以下类的实例都是相等的,但它们没有相同的toString表示形式。
public class Foo {
private String value;
@Override
public boolean equals(Object obj) {
return true;
}
@Override
public String toString() {
return "value";
}
}
在您的特定情况下,两个实例URI
是相等的,因为它们指向相同的路径,但字符串表示形式不同,因为它们之一包含冗余信息。尝试在 toString 之前使用规范化方法
看起来 equals 方法正在验证它们是否仅指向同一资源。但是,统一资源标识符字符串是不同的,因为它们以两种不同的方式访问同一资源。
这就像去一家商店用 1 美元的钞票或 4 个季度的硬币购买 1 美元的苏打水:您将使用不同的方式获得相同的苏打水。收银台的人说,无论哪种方式付款都同样好,尽管他知道钞票和一堆硬币之间有明显的区别。