Back in 2017 I posted another answer to this question which is an okay answer, but yesterday I discovered a better way of doing this.
The best way I have found is as follows:
(Note: S = superclass, D = descendant)
List<S> supers = List.copyOf( descendants );
This has the following advantages:
- It is a neat one-liner.
- It does not require the ugly
List<? extends S> construct.
- It produces no warnings.
- It does not make a copy if your list was created with
List.of() !!!
- Most importantly: it does the right thing.
Why is this the right thing?
If you look at the source code of List.copyOf() you will see that it works as follows:
- If your list was created with
List.of(), then it will do the cast and return it without copying it.
- Otherwise, (e.g. if your list is an
ArrayList(),) it will create a copy and return it.
If your original List<D> is an ArrayList<D> then a copy of the ArrayList must be made. If you were to cast the ArrayList<D> as List<S>, you would be opening up the possibility of inadvertently adding a S into that List<S>, which would then cause your original ArrayList<D> to contain a S among the Ds, which is memory corruption: attempting to iterate all the Ds in the original ArrayList<D> would throw a ClassCastException.
On the other hand, if your original List<D> has been created using List.of(), then it is unchangeable(*1), so nobody can inadvertently add an S to it, so it is okay to just cast it to List<S>.
(*1) when these lists were first introduced they were called "immutable"; later they realized that it is wrong to call them immutable, because a collection cannot be immutable, since it cannot vouch for the immutability of the elements that it contains; so they changed the documentation to call them "unmodifiable" instead; however, "unmodifiable" already had a meaning before these lists were introduced, and it meant "an unmodifiable to you view of my list which I am still free to mutate as I please, and the mutations will be very visible to you". So, neither immutable or unmodifiable is correct. I like to call them "superficially immutable" in the sense that they are not deeply immutable, but that may ruffle some feathers, so I just called them "unchangeable" as a compromise.