两个 URI 对象相等但它们的 toString() 不相等

Two URI objects are equal but their toString() are not

我不明白为什么两个文件的 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() 结果。

不过,我只是好奇,在 class 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() 输出。

没有这样的约定,好像两个对象等于字符串表示需要相同。 Java equals 实现独立于 toString 方法。例如,以下 class 的实例都是相等的,但它们没有相同的 toString 表示形式。

public class Foo {

    private String value;

    @Override
    public boolean equals(Object obj) {
        return true;
    }

    @Override
    public String toString() {
        return "value";
    }
}

在您的特定情况下,两个 URI 实例都相等,因为它们指向相同的路径,但字符串表示形式不同,因为其中一个包含冗余信息。在 toString

之前尝试使用 normalize 方法

URI#toString() 方法有据可查 here:

Returns the content of this URI as a string. If this URI was created by invoking one of the constructors in this class then a string equivalent to the original input string, or to the string computed from the originally-given components, as appropriate, is returned. Otherwise this URI was created by normalization, resolution, or relativization, and so a string is constructed from this URI's components according to the rules specified in RFC 2396, section 5.2, step 7.

意思是,toString() 方法的结果取决于我们初始化 URI 对象的方式(总共 5 constructors。)

在这种情况下,您尝试以两种不同的方式初始化 2 个 URI 对象,因此 toString() 的结果是不同的。可以试试normalize()方法。

看起来 equals 方法正在验证它们仅指向相同的 资源。但是,统一资源标识符字符串是不同的,因为它们以两种不同的方式访问同一资源。

这就像去商店用 1 美元钞票或 4 个 25 美分硬币购买 1 美元苏打水:您可以使用不同的方式获得相同的苏打水。收银台的工作人员说两种方式都可以,尽管他知道一张钞票和一堆硬币之间有明显的区别。