[External] : Re: Can we deprecate Path.endsWith(String)?
Anthony Vanelverdinghe
anthonyv.be at outlook.com
Thu Jan 15 13:56:08 UTC 2026
Thank you for taking the time to clarify the trade-offs, especially
highlighting the importance of code readers and readability.
Please bear with me and allow me to express my point of view in terms of
code authors and code readers.
Regarding code authors, I hold that when they are misled, they were
looking for a file extension API in virtually all cases. So I see a
dependency here in that adding a file extension API would reduce the
number of misleads to a negligible frequency, no larger than the
frequency with other Java SE APIs. For example, being misled to write
`Duration.from(aPeriod)`.
By deprecating these methods, I believe code authors will be misled to
simply write `path.endsWith(Path.of("foo"))`, without considering
alternative file systems (AFSs). Even code authors that actually know
about AFSs, as it's a subtle consideration that is easily overlooked.
And this will in turn impact code readers as well, as they'll wonder
whether it is acceptable to ignore AFSs (unless the code author
documented why it is). With `path.endsWith("foo")`, authors don't have
to document anything and readers don't have to wonder: it just works.
Regarding code readers, I hold that it is rare to be misled and that the
code author is best placed to judge whether `path.endsWith("foo")` is
misleading. For example, code readers would readily see that
`Files.isDirectory(path) && path.endsWith(".git")` looks for Git
repositories. And in cases where the code author considers
`path.endsWith("foo")` to be misleading, they'd simply introduce a
variable to eliminate any confusion, e.g., `var filename = "foo";
path.endsWith(filename)`.
By deprecating these methods, code authors have one less tool for
writing readable code. Since something like
`path.endsWith(path.getFileSystem().getPath("foo"))` can hardly be
considered readable, either code authors have to constrain their code to
the default file system, or they have to introduce a new local variable
for the argument to `endsWith`. In case of the former, code readers may
be left to wonder whether it is acceptable to ignore AFSs (as explained
above). In case of the latter, code readers might be misled to simplify
`var foo = path.getFileSystem().getPath("foo")` to `var foo =
Path.of("foo")`, thinking these are equivalent.
Thanks again for reading. I don't mean to drag on the discussion, so if
there's nothing in here that you hadn't already considered, we can leave
it at this.
Kind regards, Anthony
On 1/14/2026 7:20 PM, Stuart Marks wrote:
>
> You're making this too complicated.
>
> On their face, startsWith/endsWith(String) are misleading to both code
> authors and code readers, and that justifies their deprecation. It
> will help code authors avoid making new mistakes. Readers of code that
> uses these APIs -- even correctly -- can easily misinterpret the code
> as if it performed string-based testing and thus be misled about what
> the code is actually doing. In both cases, the code is better replaced
> with more explicit, if more verbose, alternatives that already exist.
>
> Certainly a file extension API would facilitate use cases that involve
> file extensions, such as inspecting a file's extension to determine
> how to process the file. I'm in favor of adding such an API. But
> that's a different topic from this one, and it should be handled
> independently.
>
> I did read all of your message but I'm not responding to most of it,
> because it doesn't establish a dependency between these two topics.
>
> s'marks
>
> On 1/13/26 12:13 PM, Anthony Vanelverdinghe wrote:
>>
>> There are 3 questions:
>>
>> (1) should we deprecate `Path::startsWith(String)`?
>> (2) should we deprecate `Path::endsWith(String)`?
>> (3) should we add a file extension API?
>>
>> And the TL;DR: no, no, yes.
>>
>> Let's first establish why `startsWith/endsWith` add tangible value:
>> because `path.startsWith("foo")` is not equivalent to
>> `path.startsWith(Path.of("foo"))`
>> and is much more readable than
>> `path.startsWith(getFileSystem().getPath("foo"))`.
>>
>> Next, let's consider why people might want to use String-based
>> `startsWith/endsWith` testing on Path instances:
>>
>> * testing file extensions = 99.9999% of the times: covered by
>> `FileSystem::getPathMatcher`
>> * testing name elements = 0.0000999% of the times: covered by `Path`
>> * any other use cases = ~0% of the times: covered by
>> `FileSystem::getPathMatcher`
>>
>> So it is always possible to do without String conversion.
>> In fact, it is arguably always a bad idea to do String-based testing,
>> because `path.toString().endsWith(".java")` will also match a file
>> named ".java",
>> which on Linux-like OSes would be considered a hidden file named
>> "java" that has no file extension.
>> So using a dedicated `PathMatcher` for testing file extensions is
>> more robust and elegant.
>>
>> However, when testing file extensions we inevitably start by typing
>> `path.`
>> (assuming we don't just use a third-party library),
>> first notice there's no method `getFileExtension` or such,
>> and then notice `endsWith(String)`
>> (and maybe we've also noticed `getFileName` and already have
>> `path.getFileName().`).
>> At this point it's pure psychology:
>> we're looking for a method that behaves like String's `endsWith(String)`,
>> we're looking at a method with the same method signature,
>> and we can't imagine that the Path class does *not* have a method to
>> test the filename extension,
>> so surely this must be it.
>> And obviously we ignore any hints at the contrary
>> (like our IDE proposing both `endsWith(Path)` and `endsWith(String)`
>> for autocompletion).
>> And we don't bother to read the Javadoc, because in cases like this
>> we can easily verify our assumptions with JShell
>> and equally quickly realize our assumptions are wrong.
>>
>> So yes, this is a common mistake. But this is actually an argument
>> for *not* deprecating it.
>> 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.
>> And I'm unable to find any previous bug reports either.
>> And here's why: when we realized our assumptions were wrong, we read
>> the Javadoc, realized our mistake, learned from it, and moved on.
>> The Javadoc is crystal-clear, the method overloads another method
>> with the same behavior, it clearly adds value over the other method.
>> In other words: we conclude "makes sense" and don't see any reason to
>> complain.
>>
>> To turn this common mistake into a rare-if-ever mistake, I see two
>> (combinable) options:
>>
>> * introduce a file extension API
>> * replace `startsWith/endsWith` with methods
>> `startsWithNames/endsWithNames`
>>
>> I don't consider deprecating `startsWith/endsWith` without
>> replacement an option because:
>>
>> * these methods add value (as was also argued by Rob Spoor), so it's
>> a net loss for the Java SE APIs.
>> 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
>> * this means breaking compilation for everyone that builds with
>> "-Werror" and "no usage of deprecated APIs" is a very common policy.
>> So people will end up adding a duplicate of the deprecated methods in
>> their own utility libraries
>> * this trades one trap for another, much more subtle trap, since
>> people will blindly replace `"foo"` with `Path.of("foo")`.
>> (We're having this very discussion because people don't read Javadoc.
>> So surely we're not expecting people to read the deprecation text and
>> follow the recommendations, are we?)
>> Eventually they'll notice there's a bug, add `IO.println(foo)` and
>> `IO.println(Path.of("foo"))`, notice these both print "foo",
>> but somehow `foo.endsWith(Path.of("foo"))` results in `false`,
>> eventually find the culprit ... and then notice the deprecated
>> `endsWith` method did exactly
>> what they wanted all along
>> * what would the rationale for the deprecation be? How would you
>> document this in the Javadoc?
>> 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."
>> But once a file extension API will be available, it'll be extremely
>> hard to come up with a reasonable justification for the deprecation.
>> And as argued above, simple String-based comparisons are rarely, if
>> ever, the most robust solution
>> * for `startsWith` in particular: the only argument to deprecate it
>> seems to be "for the sake of symmetry"
>>
>> Anthony
>>
>> On 1/12/2026 8:36 PM, Stuart Marks wrote:
>>>
>>> Let's not tie these two issues together.
>>>
>>> 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.)
>>>
>>> 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.
>>>
>>> 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.
>>>
>>> Let's proceed with just the deprecation first and work on the file
>>> extension API separately.
>>>
>>> s'marks
>>>
>>> On 1/11/26 12:45 PM, David Alayachew wrote:
>>>> 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.
>>>>
>>>> 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?
>>>>
>>>> On Sun, Jan 11, 2026 at 2:06 PM Anthony Vanelverdinghe
>>>> <anthonyv.be at outlook.com> wrote:
>>>>
>>>> I dissent. (Apparently my previous message wasn't clear.)
>>>>
>>>> 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.
>>>>
>>>> In my previous message I've already explained how these methods
>>>> add real, tangible value and actually are intuitive.
>>>> (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.)
>>>>
>>>> In hindsight, maybe `endsWithNames(String)` would've been a
>>>> better choice, but hindsight is 20/20.
>>>>
>>>> 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)`.
>>>>
>>>> Anthony
>>>>
>>>> On 1/11/2026 12:19 AM, David Alayachew wrote:
>>>>> Of course.
>>>>>
>>>>> 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?
>>>>>
>>>>> On Fri, Jan 9, 2026, 10:22 PM Brian Burkhalter
>>>>> <brian.burkhalter at oracle.com> wrote:
>>>>>
>>>>> Thanks for the corroboration.
>>>>>
>>>>>> On Jan 8, 2026, at 1:50 PM, David Alayachew
>>>>>> <davidalayachew at gmail.com> wrote:
>>>>>>
>>>>>> Thanks for reviving this.
>>>>>>
>>>>>> 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.
>>>>>>
>>>>>> 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.
>>>>>
-------------- next part --------------
An HTML attachment was scrubbed...
URL: <https://mail.openjdk.org/pipermail/core-libs-dev/attachments/20260115/b85795f3/attachment-0001.htm>
More information about the core-libs-dev
mailing list