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