<!DOCTYPE html><html><head>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8">
</head>
<body>
<p>There are 3 questions:<br>
<br>
(1) should we deprecate `Path::startsWith(String)`?<br>
(2) should we deprecate `Path::endsWith(String)`?<br>
(3) should we add a file extension API?<br>
<br>
And the TL;DR: no, no, yes.<br>
<br>
Let's first establish why `startsWith/endsWith` add tangible
value:<br>
because `path.startsWith("foo")` is not equivalent to
`path.startsWith(Path.of("foo"))`<br>
and is much more readable than
`path.startsWith(getFileSystem().getPath("foo"))`.<br>
<br>
Next, let's consider why people might want to use String-based
`startsWith/endsWith` testing on Path instances:<br>
<br>
* testing file extensions = 99.9999% of the times: covered by
`FileSystem::getPathMatcher`<br>
* testing name elements = 0.0000999% of the times: covered by
`Path`<br>
* any other use cases = ~0% of the times: covered by
`FileSystem::getPathMatcher`<br>
<br>
So it is always possible to do without String conversion.<br>
In fact, it is arguably always a bad idea to do String-based
testing,<br>
because `path.toString().endsWith(".java")` will also match a file
named ".java",<br>
which on Linux-like OSes would be considered a hidden file named
"java" that has no file extension.<br>
So using a dedicated `PathMatcher` for testing file extensions is
more robust and elegant.<br>
<br>
However, when testing file extensions we inevitably start by
typing `path.`<br>
(assuming we don't just use a third-party library),<br>
first notice there's no method `getFileExtension` or such,<br>
and then notice `endsWith(String)`<br>
(and maybe we've also noticed `getFileName` and already have
`path.getFileName().`).<br>
At this point it's pure psychology:<br>
we're looking for a method that behaves like String's
`endsWith(String)`,<br>
we're looking at a method with the same method signature,<br>
and we can't imagine that the Path class does *not* have a method
to test the filename extension,<br>
so surely this must be it.<br>
And obviously we ignore any hints at the contrary<br>
(like our IDE proposing both `endsWith(Path)` and
`endsWith(String)` for autocompletion).<br>
And we don't bother to read the Javadoc, because in cases like
this we can easily verify our assumptions with JShell<br>
and equally quickly realize our assumptions are wrong.<br>
<br>
So yes, this is a common mistake. But this is actually an argument
for *not* deprecating it.<br>
Many developers have bumped into this, but as far as I can tell
the mailing list thread in September was the first in the
existence of the API.<br>
And I'm unable to find any previous bug reports either.<br>
And here's why: when we realized our assumptions were wrong, we
read the Javadoc, realized our mistake, learned from it, and moved
on.<br>
The Javadoc is crystal-clear, the method overloads another method
with the same behavior, it clearly adds value over the other
method.<br>
In other words: we conclude "makes sense" and don't see any reason
to complain.<br>
<br>
To turn this common mistake into a rare-if-ever mistake, I see two
(combinable) options:<br>
<br>
* introduce a file extension API<br>
* replace `startsWith/endsWith` with methods
`startsWithNames/endsWithNames`<br>
<br>
I don't consider deprecating `startsWith/endsWith` without
replacement an option because:<br>
<br>
* these methods add value (as was also argued by Rob Spoor), so
it's a net loss for the Java SE APIs.<br>
And all the people that are happily using these methods today and
are unaware of this mailing list thread will be unpleasantly
surprised to see it deprecated<br>
* this means breaking compilation for everyone that builds with
"-Werror" and "no usage of deprecated APIs" is a very common
policy.<br>
So people will end up adding a duplicate of the deprecated methods
in their own utility libraries<br>
* this trades one trap for another, much more subtle trap, since
people will blindly replace `"foo"` with `Path.of("foo")`.<br>
(We're having this very discussion because people don't read
Javadoc.<br>
So surely we're not expecting people to read the deprecation text
and follow the recommendations, are we?)<br>
Eventually they'll notice there's a bug, add `IO.println(foo)` and
`IO.println(Path.of("foo"))`, notice these both print "foo",<br>
but somehow `foo.endsWith(Path.of("foo"))` results in `false`,
eventually find the culprit ... and then notice the deprecated
`endsWith` method did exactly<br>
what they wanted all along<br>
* what would the rationale for the deprecation be? How would you
document this in the Javadoc?<br>
Now you might still say: "People who were looking for a file
extension API regularly ended up here. If you're one of them, use
Path::toString instead."<br>
But once a file extension API will be available, it'll be
extremely hard to come up with a reasonable justification for the
deprecation.<br>
And as argued above, simple String-based comparisons are rarely,
if ever, the most robust solution<br>
* for `startsWith` in particular: the only argument to deprecate
it seems to be "for the sake of symmetry"<br>
<br>
Anthony</p>
<div class="moz-cite-prefix">On 1/12/2026 8:36 PM, Stuart Marks
wrote:<br>
</div>
<blockquote type="cite" cite="mid:3316a206-c9c3-4bc0-896a-2e5a03c9b472@oracle.com">
<p>Let's not tie these two issues together.</p>
<p>The discussion clearly shows that the
startsWith/endsWith(String) APIs are a trap that several people
have fallen into. On that basis it should be deprecated.
(Ordinarily, so as to emit a warning, and not for removal, so
there won't be any compatibility issue.)</p>
<p>There is also no requirement that a new API be introduced to
replace any deprecated API. As the earlier discussion in the
thread shows, both the path-based and the string-based use cases
can be written using existing APIs, somewhat less conveniently
and more verbosely; but these constructs are much more explicit
and so are preferable to the APIs to be deprecated. The
deprecation text should steer people toward the preferred
constructs.<br>
</p>
<p>It would indeed be nice to have a file extension API, but this
has been discussed several times and has run aground each time
for a variety of reasons. Tying these together will hold up the
deprecation for no good reason.</p>
<p>Let's proceed with just the deprecation first and work on the
file extension API separately.</p>
<p>s'marks<br>
</p>
<div class="moz-cite-prefix">On 1/11/26 12:45 PM, David Alayachew
wrote:<br>
</div>
<blockquote type="cite" cite="mid:CAA9v-_N24Mv5+JabZQHjuyMZVBCHE4c-_y2ByQ6omxQwyYbD3A@mail.gmail.com">
<div dir="ltr">
<div class="gmail_default" style="font-family:monospace">Thanks
for the response Anthony. Messages have been arriving
out-of-order for me, so I didn't see yours at the time of me
writing that message.</div>
<div class="gmail_default" style="font-family:monospace"><br>
</div>
<div class="gmail_default" style="font-family:monospace">I
think introducing the file extension API first, then gauging
the need for a deprecation before doing it is fine. Sounds
like then that we are universally agreed on the first step
being to add the file extension API, yes?</div>
</div>
<br>
<div class="gmail_quote gmail_quote_container">
<div dir="ltr" class="gmail_attr">On Sun, Jan 11, 2026 at
2:06 PM Anthony Vanelverdinghe <<a href="mailto:anthonyv.be@outlook.com" moz-do-not-send="true" class="moz-txt-link-freetext">anthonyv.be@outlook.com</a>>
wrote:<br>
</div>
<blockquote class="gmail_quote" style="margin:0px 0px 0px 0.8ex;border-left:1px solid rgb(204,204,204);padding-left:1ex">
<div>
<p>I dissent. (Apparently my previous message wasn't
clear.)<br>
<br>
The right order of things is to first introduce a file
extension API. Then see if there's still complaints
about `Path::endsWith(String)`. And only then, if there
are, consider taking action.<br>
<br>
In my previous message I've already explained how these
methods add real, tangible value and actually are
intuitive.<br>
(Again, ask developers to guess how `A::foo(B)` behaves,
given that both `A::foo(A)` and `B::foo(B)` exist, and a
large majority of them will intuitively guess it
converts its `b` argument to an instance of `A` and
passes it on to `A::foo(A)`. And their intuition would
be correct in the case of `Path::endsWith(String)`. That
being said, I'll be the first to admit that I've also
made the mistake of attempting to use
`Path::endsWith(String)` to test the file extension.)<br>
<br>
In hindsight, maybe `endsWithNames(String)` would've
been a better choice, but hindsight is 20/20.<br>
<br>
Deprecating these methods now is premature. And
deprecating them without replacement methods would
result in way more complaints than there have ever been
about `endsWith(String)`.<br>
<br>
Anthony</p>
<div>On 1/11/2026 12:19 AM, David Alayachew wrote:<br>
</div>
<blockquote type="cite">
<div dir="auto">Of course.
<div dir="auto"><br>
</div>
<div dir="auto">I see lots of approvals and not really
any dissenters. Are we waiting for more responses?
Or is there anything we can do to kick start this?</div>
</div>
<br>
<div class="gmail_quote">
<div dir="ltr" class="gmail_attr">On Fri, Jan 9, 2026,
10:22 PM Brian Burkhalter <<a href="mailto:brian.burkhalter@oracle.com" target="_blank" moz-do-not-send="true" class="moz-txt-link-freetext">brian.burkhalter@oracle.com</a>>
wrote:<br>
</div>
<blockquote class="gmail_quote" style="margin:0px 0px 0px 0.8ex;border-left:1px solid rgb(204,204,204);padding-left:1ex">
<div> Thanks for the corroboration.<br id="m_-8914463101179384457m_-726007501657283544lineBreakAtBeginningOfMessage">
<div><br>
<blockquote type="cite">
<div>On Jan 8, 2026, at 1:50 PM, David
Alayachew <<a href="mailto:davidalayachew@gmail.com" rel="noreferrer" target="_blank" moz-do-not-send="true" class="moz-txt-link-freetext">davidalayachew@gmail.com</a>>
wrote:</div>
<br>
<div><span style="font-family:Helvetica;font-size:12px;font-style:normal;font-variant-caps:normal;font-weight:400;letter-spacing:normal;text-align:start;text-indent:0px;text-transform:none;white-space:normal;word-spacing:0px;text-decoration:none;float:none;display:inline">Thanks
for reviving this.</span>
<div dir="auto" style="font-family:Helvetica;font-size:12px;font-style:normal;font-variant-caps:normal;font-weight:400;letter-spacing:normal;text-align:start;text-indent:0px;text-transform:none;white-space:normal;word-spacing:0px;text-decoration:none">
<br>
</div>
<div dir="auto" style="font-family:Helvetica;font-size:12px;font-style:normal;font-variant-caps:normal;font-weight:400;letter-spacing:normal;text-align:start;text-indent:0px;text-transform:none;white-space:normal;word-spacing:0px;text-decoration:none">
I am perfectly happy with the idea of
deprecating the
Path.{start,ends}With(String), and then
only add the file extension method.
Originally, I didn't know that new method
was on the table, so I suggested a rename.
But the file extension api feels like the
superior solution.</div>
<div dir="auto" style="font-family:Helvetica;font-size:12px;font-style:normal;font-variant-caps:normal;font-weight:400;letter-spacing:normal;text-align:start;text-indent:0px;text-transform:none;white-space:normal;word-spacing:0px;text-decoration:none">
<br>
</div>
<div dir="auto" style="font-family:Helvetica;font-size:12px;font-style:normal;font-variant-caps:normal;font-weight:400;letter-spacing:normal;text-align:start;text-indent:0px;text-transform:none;white-space:normal;word-spacing:0px;text-decoration:none">
10 times out of 10, if I am calling
endsWith, the only time I am not looking
for "whole" path elements is when I am
looking for a file extension. In every
other instance, the api does exactly what
I expect and want. And plus, something
like looking for a file extension is
better off being explicit.</div>
</div>
</blockquote>
</div>
</div>
</blockquote>
</div>
</blockquote>
</div>
</blockquote>
</div>
</blockquote>
</blockquote>
</body>
</html>