Search code examples
javajava-8pathjava-11nio2

Why does Path.relativize behave differently on Java 8 and Java 11?


Why does the method relativize behave differently on and ?

Path path1 = Paths.get("/a/./b/../image.png");
Path path2 = Paths.get("/a/file.txt");
Path path = path1.relativize(path2);
System.out.println(path);

The JavaDoc description of both versions is equal. I feel the way looks like a correct behavior to me:

  • path1: /a/./b/../image.png normalizes to /a/b/../image.png which normalizes to /a/image.png
  • path2: /a/file.txt
  • the way to navigate from /a/image.png and /a/file.txt is ../file.txt

Questions

  1. How is the way supposed to be calculated? Doesn't it normalize the path? I don't understand how to get the result from head.

  2. Why is there a difference between these two versions that is not documented at all?


Solution

  • Windows based source-code answer here.

    From the observation of the source codes (let's take a look at sun.nio.fs.WindowsPath, one of the implementations of Path) in is has additional code including normalization compared to .

    The key line of the latter implementation starts at the line 411, so basically, the latter implementation normalizes the paths before taking into calculation of the relative path:

    WindowsPath base = this;
    if (base.hasDotOrDotDot() || child.hasDotOrDotDot()) {
        base = base.normalize();
        child = child.normalize();
    }
    

    Digging further, the implementation changes between jdk8-b120 (source) and jdk-9+95 (source). Since the modular system was introduced, both the classes implementation and location differ:

    • Java 8 and below: /jdk/src/windows/classes/sun/nio/fs/WindowsPath.java
    • Java 9 and above: /jdk/src/java.base/windows/classes/sun/nio/fs/WindowsPath.java

    How is the way supposed to be calculated? Doesn't it normalize the path? I don't understand how to get the result from head.

    The most straightforward way to go is to normalize both paths first before relativizing them. But I have no idea whether it completely covers all the java.nio.file.Path implementations and is safe to do so.

    Path path = path1.normalize().relativize(path2.normalize());