At some point, during your exhaustive studies of Java NIO, you might have encountered the methods relativize() and resolve(). And you might have wondered why they exist and what they do.

I will solve that mystery for you here.

And I think you'll find that they're both fairly important concepts if you want to use the Path class correctly.

In a Nutshell

So here's what you need to know.

First of all, they're both part of the Path class. So you'll need to work with Paths if you want to take advantage of the benefits offered by relativize() and resolve().

Next, I'll explain the difference:

  • relativize() strips away the root directory leaving only the subdirectory or subdirectories
  • resolve() prepends a relative Path object with a root directory and creates a whole new structure

For example, the relativize() method will take /my/cool/directory and transform it to a Path object that points to cool/directory.

The resolve() method will take cool/directory and prepend it with a Path object that points to /other to create a new Path object that points to /other/cool/directory.

So resovle() giveth while relativize() taketh away.

If you need more details, read on.

A Sample Use Case

I just finished an guide on how to use Java NIO to recursively copy an entire directory. That solution uses a custom FileVisitor implementation that in turn uses the relativize() and resolve() methods of the Path class.

Take a look at the code:

private static class CustomFileVisitor extends SimpleFileVisitor<Path> {
    
    private Path source;
    private Path target;
    private CopyOption[] options;
    
    private CustomFileVisitor(Path source, Path target, CopyOption... options) {
        this.target = target;
        this.source = source;
        this.options = options;
    }
    
    @Override
    public FileVisitResult preVisitDirectory(Path dir, BasicFileAttributes attrs)
            throws IOException {
        Files.createDirectories(target.resolve(source.relativize(dir)));
        return FileVisitResult.CONTINUE;
    }

    @Override
    public FileVisitResult visitFile(Path file, BasicFileAttributes attrs)
            throws IOException {
        Files.copy(file, target.resolve(source.relativize(file)), options);
        return FileVisitResult.CONTINUE;
    }        
}

Keep in mind: the whole point of that class is to copy a directory bit-by-bit recursively. Remember that as you go through the code.

Hone in on the two overriden methods. Each of them uses relativize() and resolve().

In fact, they're used together. That might confuse you even more.

So what's going on?

Take a look at preVisitDirectory(). It accepts two parameters: a Path object and a BasicFileAttributes object.

Ignore the second parameter for now. It's not important for this guide.

But the first parameter is a Path object that reflects a source directory. The code will use that source directory to create a new subdirectory in the target directory. It's all part of the recursive copy process.

But first, it needs the name of the target directory. So it builds that name based on the name of the source directory.

That's where those two methods come into play.

Let's say the code is copying everything from a directory named /source to a directory named /target.

And let's further say that the preVisitDirectory() method gets run with the dir parameter equal to a directory called /source/brian.

That means the Files.createDirectories() method needs to create a directory called /target/brian.

But it has to determine that directory name based on the name of the source directory (/source/brian).

So it does that with a two-step process.

In the first step, it uses relativize() to get just the subdirectory off the root of the source directory.

So that source.relativize(dir) that you see above will return brian.

Note that it stripped away the /source. That's what relativize() does.

But that's not all the info the code needs. The code needs the new parent directory so it can create this subdirectory.

That's why it calls resolve() from target. And that would be Step 2, by the way.

The target property, by itself, is equal to /target. And it's also a Path object.

So the JVM will take /target and resolve() it with brian. The end result is a new Path object that points to /target/brian.

And that's exactly what you want.

Wrapping It Up

That's it. That's what you need to know about relativize() and resolve().

If you're developing a Java solution that requires quite a bit of file I/O, I think you'll find both of those methods useful.

Have fun!