<div dir="ltr">Hi all,<br><br>I am running into an issue with the collections framework where I have to choose between good semantics for users and performance.<br><br>Specifically I am taking a java.util.List from my users and I need to choose to either<br>* Not defensively copy and expose a potential footgun when I pass that List to another thread<br>* Defensively copy and make my users pay an unnecessary runtime cost.<br><br>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().<br><br>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.<br><br><a href="https://www.reddit.com/r/java/comments/sf8qrv/comment/hv8or92/?utm_source=share&utm_medium=web2x&context=3">https://www.reddit.com/r/java/comments/sf8qrv/comment/hv8or92/?utm_source=share&utm_medium=web2x&context=3</a> <br><br>Important also for context is Ron Pressler's comment above.<br>--------------<br><br><div><div class="gmail-"><div class="gmail-public-DraftStyleDefault-block gmail-public-DraftStyleDefault-ltr">What if the collections api added more marker interfaces like <span style="font-family:"Noto Mono",Menlo,Monaco,Consolas,monospace;padding:0.1em 0.2em;font-size:0.8em;border-radius:3px">RandomAccess</span>?<br><br></div></div><div class="gmail-"><div class="gmail-public-DraftStyleDefault-block gmail-public-DraftStyleDefault-ltr">It's already a common thing for codebases to make explicit null checks at error boundaries because the type system can't encode <span style="font-family:"Noto Mono",Menlo,Monaco,Consolas,monospace;padding:0.1em 0.2em;font-size:0.8em;border-radius:3px">null | List<String></span>. <br><br>This feels like a similar problem.</div></div><div class="gmail-"><div class="gmail-public-DraftStyleDefault-block gmail-public-DraftStyleDefault-ltr">If you have a <span style="font-family:"Noto Mono",Menlo,Monaco,Consolas,monospace;padding:0.1em 0.2em;font-size:0.8em;border-radius:3px">List<T></span> 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.</div></div><div class="gmail-"><div class="gmail-public-DraftStyleDefault-block gmail-public-DraftStyleDefault-ltr"><br></div></div>
<table>
<thead>
<tr><th align="left" style="margin:0px;padding:4px 9px;border:1px solid rgb(229,227,218)"><div class="gmail-"><div class="gmail-public-DraftStyleDefault-block gmail-public-DraftStyleDefault-ltr">If the List is actually a __</div></div></th><th align="left" style="margin:0px;padding:4px 9px;border:1px solid rgb(229,227,218)"><div class="gmail-"><div class="gmail-public-DraftStyleDefault-block gmail-public-DraftStyleDefault-ltr">Then you can definitely call</div></div></th><th align="left" style="margin:0px;padding:4px 9px;border:1px solid rgb(229,227,218)"><div class="gmail-"><div class="gmail-public-DraftStyleDefault-block gmail-public-DraftStyleDefault-ltr">And you know other reference holders might call</div></div></th><th align="left" style="margin:0px;padding:4px 9px;border:1px solid rgb(229,227,218)"><div class="gmail-"><div class="gmail-public-DraftStyleDefault-block gmail-public-DraftStyleDefault-ltr">And you can confirm its this case by</div></div></th>
</tr>
</thead>
<tbody>
<tr><td align="left" style="padding:4px 9px;border:1px solid rgb(229,227,218)"><div class="gmail-"><div class="gmail-public-DraftStyleDefault-block gmail-public-DraftStyleDefault-ltr">null</div></div></td><td align="left" style="padding:4px 9px;border:1px solid rgb(229,227,218)"><div class="gmail-"><div class="gmail-public-DraftStyleDefault-block gmail-public-DraftStyleDefault-ltr">no methods</div></div></td><td align="left" style="padding:4px 9px;border:1px solid rgb(229,227,218)"><div class="gmail-"><div class="gmail-public-DraftStyleDefault-block gmail-public-DraftStyleDefault-ltr">no methods</div></div></td><td align="left" style="padding:4px 9px;border:1px solid rgb(229,227,218)"><div class="gmail-"><div class="gmail-public-DraftStyleDefault-block gmail-public-DraftStyleDefault-ltr">list == null</div></div></td>
</tr>
<tr><td align="left" style="padding:4px 9px;border:1px solid rgb(229,227,218)"><div class="gmail-"><div class="gmail-public-DraftStyleDefault-block gmail-public-DraftStyleDefault-ltr">List.of(...)</div></div></td><td align="left" style="padding:4px 9px;border:1px solid rgb(229,227,218)"><div class="gmail-"><div class="gmail-public-DraftStyleDefault-block gmail-public-DraftStyleDefault-ltr">get, size</div></div></td><td align="left" style="padding:4px 9px;border:1px solid rgb(229,227,218)"><div class="gmail-"><div class="gmail-public-DraftStyleDefault-block gmail-public-DraftStyleDefault-ltr">get, size</div></div></td><td align="left" style="padding:4px 9px;border:1px solid rgb(229,227,218)"><div class="gmail-"><div class="gmail-public-DraftStyleDefault-block gmail-public-DraftStyleDefault-ltr">???</div></div></td>
</tr>
<tr><td align="left" style="padding:4px 9px;border:1px solid rgb(229,227,218)"><div class="gmail-"><div class="gmail-public-DraftStyleDefault-block gmail-public-DraftStyleDefault-ltr">Collections.unmodifiableList(...)</div></div></td><td align="left" style="padding:4px 9px;border:1px solid rgb(229,227,218)"><div class="gmail-"><div class="gmail-public-DraftStyleDefault-block gmail-public-DraftStyleDefault-ltr">get, size</div></div></td><td align="left" style="padding:4px 9px;border:1px solid rgb(229,227,218)"><div class="gmail-"><div class="gmail-public-DraftStyleDefault-block gmail-public-DraftStyleDefault-ltr">get, size, add, set</div></div></td><td align="left" style="padding:4px 9px;border:1px solid rgb(229,227,218)"><div class="gmail-"><div class="gmail-public-DraftStyleDefault-block gmail-public-DraftStyleDefault-ltr">???</div></div></td>
</tr>
<tr><td align="left" style="padding:4px 9px;border:1px solid rgb(229,227,218)"><div class="gmail-"><div class="gmail-public-DraftStyleDefault-block gmail-public-DraftStyleDefault-ltr">Arrays.asList(...)</div></div></td><td align="left" style="padding:4px 9px;border:1px solid rgb(229,227,218)"><div class="gmail-"><div class="gmail-public-DraftStyleDefault-block gmail-public-DraftStyleDefault-ltr">get, size, set</div></div></td><td align="left" style="padding:4px 9px;border:1px solid rgb(229,227,218)"><div class="gmail-"><div class="gmail-public-DraftStyleDefault-block gmail-public-DraftStyleDefault-ltr">get, size, set</div></div></td><td align="left" style="padding:4px 9px;border:1px solid rgb(229,227,218)"><div class="gmail-"><div class="gmail-public-DraftStyleDefault-block gmail-public-DraftStyleDefault-ltr">???</div></div></td>
</tr>
<tr><td align="left" style="padding:4px 9px;border:1px solid rgb(229,227,218)"><div class="gmail-"><div class="gmail-public-DraftStyleDefault-block gmail-public-DraftStyleDefault-ltr">new ArrayList<>()</div></div></td><td align="left" style="padding:4px 9px;border:1px solid rgb(229,227,218)"><div class="gmail-"><div class="gmail-public-DraftStyleDefault-block gmail-public-DraftStyleDefault-ltr">get, size, add, set</div></div></td><td align="left" style="padding:4px 9px;border:1px solid rgb(229,227,218)"><div class="gmail-"><div class="gmail-public-DraftStyleDefault-block gmail-public-DraftStyleDefault-ltr">get, size, add, set</div></div></td><td align="left" style="padding:4px 9px;border:1px solid rgb(229,227,218)"><div class="gmail-"><div class="gmail-public-DraftStyleDefault-block gmail-public-DraftStyleDefault-ltr">???</div></div></td>
</tr>
</tbody>
</table><div class="gmail-"><div class="gmail-public-DraftStyleDefault-block gmail-public-DraftStyleDefault-ltr">While yes, there is no feasible way to encode these things in the type system. Its not impossible to encode it at runtime though.</div></div><code class="gmail-"><div class="gmail-public-DraftStyleDefault-block gmail-public-DraftStyleDefault-ltr">interface FullyImmutable {</div></code><code class="gmail-"><div class="gmail-public-DraftStyleDefault-block gmail-public-DraftStyleDefault-ltr"> // So you know the existence of this implies the absence</div></code><code class="gmail-"><div class="gmail-public-DraftStyleDefault-block gmail-public-DraftStyleDefault-ltr"> // of the others </div></code><code class="gmail-"><div class="gmail-public-DraftStyleDefault-block gmail-public-DraftStyleDefault-ltr"> default Void cantIntersect() { return null; }</div></code><code class="gmail-"><div class="gmail-public-DraftStyleDefault-block gmail-public-DraftStyleDefault-ltr">}</div></code><code class="gmail-"><div class="gmail-public-DraftStyleDefault-block gmail-public-DraftStyleDefault-ltr"><br></div></code><code class="gmail-"><div class="gmail-public-DraftStyleDefault-block gmail-public-DraftStyleDefault-ltr">interace MutationCapability {</div></code><code class="gmail-"><div class="gmail-public-DraftStyleDefault-block gmail-public-DraftStyleDefault-ltr"> default String cantIntersect() { return ""; }</div></code><code class="gmail-"><div class="gmail-public-DraftStyleDefault-block gmail-public-DraftStyleDefault-ltr">}</div></code><code class="gmail-"><div class="gmail-public-DraftStyleDefault-block gmail-public-DraftStyleDefault-ltr"><br></div></code><code class="gmail-"><div class="gmail-public-DraftStyleDefault-block gmail-public-DraftStyleDefault-ltr">interface Addable extends MutationCapability {}</div></code><code class="gmail-"><div class="gmail-public-DraftStyleDefault-block gmail-public-DraftStyleDefault-ltr">interface Settable extends MutationCapability {}</div></code><div class="gmail-"><div class="gmail-public-DraftStyleDefault-block gmail-public-DraftStyleDefault-ltr"><br></div></div>
<table>
<thead>
<tr><th align="left" style="margin:0px;padding:4px 9px;border:1px solid rgb(229,227,218)"><div class="gmail-"><div class="gmail-public-DraftStyleDefault-block gmail-public-DraftStyleDefault-ltr">If the List is actually a __</div></div></th><th align="left" style="margin:0px;padding:4px 9px;border:1px solid rgb(229,227,218)"><div class="gmail-"><div class="gmail-public-DraftStyleDefault-block gmail-public-DraftStyleDefault-ltr">Then you can definitely call</div></div></th><th align="left" style="margin:0px;padding:4px 9px;border:1px solid rgb(229,227,218)"><div class="gmail-"><div class="gmail-public-DraftStyleDefault-block gmail-public-DraftStyleDefault-ltr">And you know other reference holders might call</div></div></th><th align="left" style="margin:0px;padding:4px 9px;border:1px solid rgb(229,227,218)"><div class="gmail-"><div class="gmail-public-DraftStyleDefault-block gmail-public-DraftStyleDefault-ltr">And you can confirm its this case by</div></div></th>
</tr>
</thead>
<tbody>
<tr><td align="left" style="padding:4px 9px;border:1px solid rgb(229,227,218)"><div class="gmail-"><div class="gmail-public-DraftStyleDefault-block gmail-public-DraftStyleDefault-ltr">null</div></div></td><td align="left" style="padding:4px 9px;border:1px solid rgb(229,227,218)"><div class="gmail-"><div class="gmail-public-DraftStyleDefault-block gmail-public-DraftStyleDefault-ltr">no methods</div></div></td><td align="left" style="padding:4px 9px;border:1px solid rgb(229,227,218)"><div class="gmail-"><div class="gmail-public-DraftStyleDefault-block gmail-public-DraftStyleDefault-ltr">no methods</div></div></td><td align="left" style="padding:4px 9px;border:1px solid rgb(229,227,218)"><div class="gmail-"><div class="gmail-public-DraftStyleDefault-block gmail-public-DraftStyleDefault-ltr">list == null</div></div></td>
</tr>
<tr><td align="left" style="padding:4px 9px;border:1px solid rgb(229,227,218)"><div class="gmail-"><div class="gmail-public-DraftStyleDefault-block gmail-public-DraftStyleDefault-ltr">List.of(...)</div></div></td><td align="left" style="padding:4px 9px;border:1px solid rgb(229,227,218)"><div class="gmail-"><div class="gmail-public-DraftStyleDefault-block gmail-public-DraftStyleDefault-ltr">get, size</div></div></td><td align="left" style="padding:4px 9px;border:1px solid rgb(229,227,218)"><div class="gmail-"><div class="gmail-public-DraftStyleDefault-block gmail-public-DraftStyleDefault-ltr">get, size</div></div></td><td align="left" style="padding:4px 9px;border:1px solid rgb(229,227,218)"><div class="gmail-"><div class="gmail-public-DraftStyleDefault-block gmail-public-DraftStyleDefault-ltr">instanceof FullyImmutable</div></div></td>
</tr>
<tr><td align="left" style="padding:4px 9px;border:1px solid rgb(229,227,218)"><div class="gmail-"><div class="gmail-public-DraftStyleDefault-block gmail-public-DraftStyleDefault-ltr">Collections.unmodifiableList(...)</div></div></td><td align="left" style="padding:4px 9px;border:1px solid rgb(229,227,218)"><div class="gmail-"><div class="gmail-public-DraftStyleDefault-block gmail-public-DraftStyleDefault-ltr">get, size</div></div></td><td align="left" style="padding:4px 9px;border:1px solid rgb(229,227,218)"><div class="gmail-"><div class="gmail-public-DraftStyleDefault-block gmail-public-DraftStyleDefault-ltr">get, size, add, set</div></div></td><td align="left" style="padding:4px 9px;border:1px solid rgb(229,227,218)"><div class="gmail-"><div class="gmail-public-DraftStyleDefault-block gmail-public-DraftStyleDefault-ltr">!(instanceof Addable) && !(instanceof Settable)</div></div></td>
</tr>
<tr><td align="left" style="padding:4px 9px;border:1px solid rgb(229,227,218)"><div class="gmail-"><div class="gmail-public-DraftStyleDefault-block gmail-public-DraftStyleDefault-ltr">Arrays.asList(...)</div></div></td><td align="left" style="padding:4px 9px;border:1px solid rgb(229,227,218)"><div class="gmail-"><div class="gmail-public-DraftStyleDefault-block gmail-public-DraftStyleDefault-ltr">get, size, set</div></div></td><td align="left" style="padding:4px 9px;border:1px solid rgb(229,227,218)"><div class="gmail-"><div class="gmail-public-DraftStyleDefault-block gmail-public-DraftStyleDefault-ltr">get, size, set</div></div></td><td align="left" style="padding:4px 9px;border:1px solid rgb(229,227,218)"><div class="gmail-"><div class="gmail-public-DraftStyleDefault-block gmail-public-DraftStyleDefault-ltr">instanceof Settable</div></div></td>
</tr>
<tr><td align="left" style="padding:4px 9px;border:1px solid rgb(229,227,218)"><div class="gmail-"><div class="gmail-public-DraftStyleDefault-block gmail-public-DraftStyleDefault-ltr">new ArrayList<>()</div></div></td><td align="left" style="padding:4px 9px;border:1px solid rgb(229,227,218)"><div class="gmail-"><div class="gmail-public-DraftStyleDefault-block gmail-public-DraftStyleDefault-ltr">get, size, add, set</div></div></td><td align="left" style="padding:4px 9px;border:1px solid rgb(229,227,218)"><div class="gmail-"><div class="gmail-public-DraftStyleDefault-block gmail-public-DraftStyleDefault-ltr">get, size, add, set</div></div></td><td align="left" style="padding:4px 9px;border:1px solid rgb(229,227,218)"><div class="gmail-"><div class="gmail-public-DraftStyleDefault-block gmail-public-DraftStyleDefault-ltr">instanceof Settable && instanceof Addable</div></div></td>
</tr>
</tbody>
</table><div class="gmail-"><div class="gmail-public-DraftStyleDefault-block gmail-public-DraftStyleDefault-ltr">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.<br><br><br>--------------<br><br>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.<br><br><br></div></div></div></div>