Proposal: Collection mutability marker interfaces

Ethan McCue ethan at mccue.dev
Tue Aug 23 23:49:23 UTC 2022


Hi all,

I am running into an issue with the collections framework where I have to
choose between good semantics for users and performance.

Specifically I am taking a java.util.List from my users and I need to
choose to either
* Not defensively copy and expose a potential footgun when I pass that List
to another thread
* Defensively copy and make my users pay an unnecessary runtime cost.

What I would really want, in a nutshell, is for List.copyOf to be a no-op
when used on lists made with List.of().

Below the line is a pitch I wrote up on reddit 7 months ago for a mechanism
I think could accomplish that. My goal is to share the idea a bit more
widely and to this specific audience to get feedback.

https://www.reddit.com/r/java/comments/sf8qrv/comment/hv8or92/?utm_source=share&utm_medium=web2x&context=3


Important also for context is Ron Pressler's comment above.
--------------

What if the collections api added more marker interfaces like RandomAccess?

It's already a common thing for codebases to make explicit null checks at
error boundaries because the type system can't encode null | List<String>.

This feels like a similar problem.
If you have a List<T> in the type system then you don't know for sure you
can call any methods on it until you check that its not null. In the same
way, there is a set of methods that you don't know at the type/interface
level if you are allowed to call.

If the List is actually a __
Then you can definitely call
And you know other reference holders might call
And you can confirm its this case by
null
no methods
no methods
list == null
List.of(...)
get, size
get, size
???
Collections.unmodifiableList(...)
get, size
get, size, add, set
???
Arrays.asList(...)
get, size, set
get, size, set
???
new ArrayList<>()
get, size, add, set
get, size, add, set
???
While yes, there is no feasible way to encode these things in the type
system. Its not impossible to encode it at runtime though.
interface FullyImmutable {
// So you know the existence of this implies the absence
// of the others
default Void cantIntersect() { return null; }
}

interace MutationCapability {
default String cantIntersect() { return ""; }
}

interface Addable extends MutationCapability {}
interface Settable extends MutationCapability {}

If the List is actually a __
Then you can definitely call
And you know other reference holders might call
And you can confirm its this case by
null
no methods
no methods
list == null
List.of(...)
get, size
get, size
instanceof FullyImmutable
Collections.unmodifiableList(...)
get, size
get, size, add, set
!(instanceof Addable) && !(instanceof Settable)
Arrays.asList(...)
get, size, set
get, size, set
instanceof Settable
new ArrayList<>()
get, size, add, set
get, size, add, set
instanceof Settable && instanceof Addable
In the same way a RandomAccess check let's implementations decide whether
they want to try an alternative algorithm or crash, some marker
"capability" interfaces would let users of a collection decide if they want
to clone what they are given before working on it.


--------------

So the applicability of this would be that the list returned by List.of
could implement FullyImmutable, signifying that there is no caller which
might have a mutable handle on the collection. Then List.of could check for
this interface and skip a copy.
-------------- next part --------------
An HTML attachment was scrubbed...
URL: <https://mail.openjdk.org/pipermail/core-libs-dev/attachments/20220823/b4018928/attachment-0001.htm>


More information about the core-libs-dev mailing list