<div dir="ltr"><div dir="ltr">I quite agree with you. I think the collection framework is in a very puzzling state.</div><div dir="ltr"><br></div><div dir="ltr"><div>The hostility of the new collection factory method introduced by Java 9 to null has brought us trouble.<br></div><div>I can understand that this is to expect users to explicitly declare that the elements of the list cannot be null, rather than in an ambiguous state.<br></div><div>However, this makes migration more difficult, and the cost of scanning all elements to ensure that they are not null is also unpleasant.<br></div><div>I hope to provide an additional set of factory methods for the collection to accept nullable elements.<br></div><div><br></div></div><div dir="ltr"><div>In addition, it is also confusing to share the same interface between mutable collections and immutable collections .<br></div><div>Before encountering UnsupportedOperationException, we can't even know what operations a collection supports.<br></div><div>This problem has existed for a long time, but no solution has been given.<br></div><div><br></div></div><br><div class="gmail_quote"><div dir="ltr" class="gmail_attr">On Sun, Jan 29, 2023 at 11:28 PM John Hendrikx <<a href="mailto:john.hendrikx@gmail.com">john.hendrikx@gmail.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">TLDR; why does contains(null) not just return false for collections that <br>
don't allow nulls. Just because the interface allows it, does not mean <br>
it should do it as it devalues the usefulness of the abstraction <br>
provided by the interface.<br>
<br>
Background:<br>
<br>
I'm someone that likes to check correctness of any constructor or method <br>
parameter before allowing an object to be constructed or to be modified, <br>
in order to maintain invariants that are provided by the class to its users.<br>
<br>
This ranges from simple null checks, to range checks on numeric values, <br>
to complete checks like "is a collection sorted" or is a list of nodes <br>
acyclic. Anything I can check in the constructor that may avoid problems <br>
further down the line or that may affect what I can guarantee on its own <br>
API methods.  For example, if I check in the constructor that something <br>
is not null, then the associated getter will guarantee that the returned <br>
value is not null.  If I check that a List doesn't contain nulls, then <br>
the associated getter will reflect that as well (assuming it is <br>
immutable or defensivily copied).<br>
<br>
For collections, this is currently becoming harder and harder because <br>
more and more new collections are written to be null hostile.  It is <br>
fine if a collection doesn't accept nulls, but throwing NPE when I ask <br>
such a collection if it contains null seems to be counter productive, <br>
and reduces the usefulness of the collection interfaces.<br>
<br>
This strict interpretation makes the collection interfaces harder to use <br>
than necessary. Interfaces are only useful when their contract is well <br>
defined. The more things an interface allows or leaves unspecified, the <br>
less useful it is for its users.<br>
<br>
I know that the collection interfaces allow this, but you have to ask <br>
yourself this important question: how useful is an interface that makes <br>
almost no guarantees about what its methods will do? Interfaces like <br>
`List`, `Map` and `Set` are passed as method parameters a lot, and to <br>
make these useful, implementations of these interfaces should do their <br>
best to provide as consistent an experience as is reasonably possible.  <br>
The alternative is that these interfaces will slowly decline in <br>
usefulness as methods will start asking for `ArrayList` instead of <br>
`List` to avoid having to deal with a too lenient specification.<br>
<br>
With the collection interfaces I get the impression that recently there <br>
has been too much focus on what would be easy for the collection <br>
implementation instead of what would be easy for the users of said <br>
interfaces. In my opinion, the concerns of the user of interfaces almost <br>
always far outweigh the concerns of the implementors of said interfaces.<br>
<br>
In the past, immutable collections were rare, but they get are getting <br>
more and more common all the time.  For example, in unit testing. <br>
Unfortunately, these immutable collections differ quite a bit from their <br>
mutable counterparts.  Some parts are only midly annoying (not accepting <br>
`null` as the **value** in a `Map` for example), but other parts require <br>
code to be rewritten for it to be able to work as a drop-in replacement <br>
for the mutable variant. A simple example is this:<br>
<br>
     public void addShoppingItems(Collection<String> shoppingItems) {<br>
         if (shoppingItems.contains(null)) {  // this looks quite <br>
reasonable and safe...<br>
             throw new IllegalArgumentException("shoppingItems should <br>
not contain nulls");<br>
         }<br>
<br>
         this.shoppingItems.addAll(shoppingItems);<br>
     }<br>
<br>
Testing this code is annoying:<br>
<br>
      x.addShoppingItems(List.of("a", null"));   // can't construct <br>
immutable collection with null<br>
<br>
      x.addShoppingItems(Arrays.asList("a", null"));  // fine, go back <br>
to what we did before then...<br>
<br>
The above problems, I suppose we can live with it; immutable collections <br>
don't want `null` although I don't see any reason to not allow it as I <br>
can write a similar `List.of` that returns immutable collections that do <br>
allow `null`. For JDK code this is a bit disconcerting, as it is <br>
supposed to be as flexible as is reasonable without having too much of <br>
an opinion about what is good or bad.<br>
<br>
This next one however:<br>
<br>
      assertNoExceptionThrown(() -> x.addShoppingItems(List.of("a", <br>
"b")));  // not allowed, contains(null) in application code throws NPE<br>
<br>
This is much more insidious; the `contains(null)` check has been a very <br>
practical way to check if collections contain null, and this works for <br>
almost all collections in common use, so there is no problem.  But now, <br>
more and more collections are starting to just throw NPE immediately <br>
even just to **check** if a null element is present. This only serves to <br>
distinguish themselves loudly from other similar collections that will <br>
simply return `false`.<br>
<br>
I think this behavior is detrimental to the java collection interfaces <br>
in general, especially since there is a clear answer to the question if <br>
such a collection contains null or not. In fact, why `contains` was ever <br>
allowed to throw an exception aside from "UnsupportedOperationException" <br>
is a mystery to me, and throwing one when one could just as easily <br>
return the correct and expected answer seems very opiniated and almost <br>
malicious -- not behavior I'd expect from JDK core libraries.<br>
<br>
Also note that there is no way to know in advance if `contains(null)` is <br>
going to be throwing the NPE. If interfaces had a method `allowsNulls()` <br>
that would already be an improvement.<br>
<br>
--John<br>
<br>
<br>
</blockquote></div></div>