Give a concrete example of the Visitor pattern in the JDK and explain how it works there.
answer
- Files.walkFileTree + FileVisitor
- Callbacks: preVisitDirectory / visitFile / postVisitDirectory
- FileVisitResult steers the walk (CONTINUE/SKIP/TERMINATE)
- SimpleFileVisitor base class
- javax.lang.model = true accept/visit double dispatch
basics
~20 sThe NIO file API uses it: Files.walkFileTree takes a FileVisitor. As the JDK walks a directory tree, it calls your visitor's methods (visitFile, preVisitDirectory, etc.) for each entry, so you supply the operation and the JDK supplies the traversal.
solid answer
~40 sThe clearest JDK example is java.nio.file.FileVisitor used by Files.walkFileTree. You implement FileVisitor (or extend SimpleFileVisitor) with callbacks: preVisitDirectory, visitFile, visitFileFailed, postVisitDirectory. Files.walkFileTree owns the traversal of the directory tree (the object structure) and calls your visitor at each node, returning a FileVisitResult (CONTINUE, SKIP_SUBTREE, TERMINATE) to steer the walk. This is Visitor in spirit: the structure traversal is decoupled from the operation, and you can add a new operation (delete tree, copy tree, search) just by writing a new visitor without changing the walking code. It isn't textbook double dispatch over a sealed element hierarchy — the elements are uniform Path/attributes — but it embodies the same separation-of-operation-from-structure intent. Other examples: AnnotationValueVisitor / ElementVisitor / TypeVisitor in javax.lang.model (annotation processing), which are true double-dispatch visitors with accept().
go deeper
Can name Files.walkFileTree/FileVisitor and describe that the JDK walks the tree and calls your callbacks.
Explains the callbacks and FileVisitResult control flow, and that operations are added without changing the walker.
Distinguishes the practical FileVisitor (no accept) from the strict javax.lang.model visitors (true double dispatch).
Discusses why the JDK chose a loosened visitor for uniform Path nodes vs strict double dispatch for the heterogeneous language-model element hierarchy.
## Why look at JDK examples Seeing a pattern in the standard library proves it isn't academic. The JDK has both a *practical, loosened* Visitor and a *textbook, strict* one. ## Example 1 — NIO file tree walking (practical Visitor) `java.nio.file.Files.walkFileTree(Path start, FileVisitor<? super Path> visitor)` walks a directory tree. You provide a **FileVisitor** — an object that packages the *operation* you want performed at each entry: ```java interface FileVisitor<T> { FileVisitResult preVisitDirectory(T dir, BasicFileAttributes attrs); FileVisitResult visitFile(T file, BasicFileAttributes attrs); FileVisitResult visitFileFailed(T file, IOException exc); FileVisitResult postVisitDirectory(T dir, IOException exc); } ``` The JDK owns the **object structure** (the file system tree) and the **traversal**; *you* own the **operation** (what to do at each file/dir). Each callback returns a **FileVisitResult** enum that *steers* the walk: `CONTINUE`, `SKIP_SUBTREE`, `SKIP_SIBLINGS`, `TERMINATE`. `SimpleFileVisitor<T>` is a convenience base class with no-op defaults so you override only what you need. This is the **Visitor intent**: separate the operation from the structure, so new operations (delete a tree, copy a tree, find big files) are new visitor classes with **zero changes** to the walking engine. It is *not* the strict GoF form — there's no `accept()` and no per-type `visit` overloads, because every node is a uniform `Path` rather than a sealed family of distinct element classes. ```java Files.walkFileTree(start, new SimpleFileVisitor<Path>() { @Override public FileVisitResult visitFile(Path file, BasicFileAttributes a) throws IOException { Files.delete(file); return FileVisitResult.CONTINUE; } @Override public FileVisitResult postVisitDirectory(Path dir, IOException e) throws IOException { Files.delete(dir); return FileVisitResult.CONTINUE; } }); ``` ## Example 2 — javax.lang.model visitors (textbook Visitor) The annotation-processing API (used by tools and libraries that read your source at compile time) has **true** double-dispatch visitors: `ElementVisitor`, `TypeVisitor`, `AnnotationValueVisitor`. Each model element has an `accept(Visitor, P)` method, and the visitor has one `visitXxx` method per element kind (`visitType`, `visitExecutable`, `visitVariable`, …). Convenience base classes like `SimpleElementVisitor14` supply defaults. This is the strict element-hierarchy + accept/visit form. ## Other honorable mentions - Older Swing/AWT and `java.util.regex` internals use visitor-like dispatch. - DOM/XML `NodeVisitor`-style APIs in many libraries follow the same shape. ## Takeaway When you see an API where *you hand it a callback object and it drives the traversal*, you're usually looking at Visitor (or its close cousin). `FileVisitor` is the one most interviewers expect you to name; `javax.lang.model`'s visitors are the strict, accept-based form to cite when asked for genuine double dispatch in the JDK.
- How do you stop a file tree walk early?Return FileVisitResult.TERMINATE from a visitor callback; return SKIP_SUBTREE to skip a directory's children, or SKIP_SIBLINGS to skip the rest of the current directory.
- Is FileVisitor a strict GoF Visitor?No — it captures the intent (operation separated from traversal) but lacks accept()/per-type visit overloads because all nodes are the same Path type. javax.lang.model's ElementVisitor is the strict, double-dispatch form.
saying these in an interview costs you the question
- Claiming FileVisitor uses accept()/double dispatch (it doesn't — nodes are uniform Path)
- Forgetting to return a FileVisitResult from callbacks
- Not naming any concrete JDK example when asked
- Confusing FileVisitor with the Iterator pattern