Changing Collections.unmodifiableList
to return List<? extends T>
instead of List<T>
would then prevent adding and removing an element in compile time instead of throwing a runtime exception.
Is there a critical problem caused by this alternative that would preclude it?
The fundamental reason that Collections.unmodifiableList
doesn't return List<? extends T>
is that it's the wrong return type. First a bit of background. The current declaration is
static <T> List<T> unmodifiableList(List<? extends T> list)
Note that this takes a List<? extends T>
but returns List<T>
. This conforms to Bloch's PECS principle (also known to Naftalin/Wadler as the put and get principle). Since the returned list is unmodifiable, you can only take things out of it, hence it's a "producer" of elements of type T.
This provides the caller a bit more flexibility as to the return type. For instance, one could do this:
List<Double> src = Arrays.asList(1.0, 2.0, 3.0);
List<Number> list1 = Collections.unmodifiableList(src);
This is useful in case there are already other functions, for example, that operate on List<Number>
.
Now consider an alternative declaration:
static <T> List<? extends T> wildUnmodifiableList(List<? extends T> list)
This says something quite different. It means that it takes a list of some subtype of T and returns a list of some unknown subtype of T, which might differ from the first type. Since the returned list is an unmodifiable view of the first list, this doesn't make any sense. Let's illustrate with an example:
List<Double> src = Arrays.asList(1.0, 2.0, 3.0);
List<? extends Number> list2 = wildUnmodifiableList(src);
This means that we pass in a List<Double>
but we might get back a list of some other type, perhaps List<Integer>
. Again, that doesn't really make any sense.
You can see that this declaration is incorrect when you try to use it. Consider the IterableUtils.frequency
method from Apache Commons Collections:
static <E,T extends E> int frequency(Iterable<E> iterable, T obj)
This works great with the current declaration:
List<Double> src = Arrays.asList(1.0, 2.0, 3.0);
List<Number> list1 = Collections.unmodifiableList(src);
int freq1 = frequency(list1, 0.0);
But it fails with the wildcard version:
List<Double> src = Arrays.asList(1.0, 2.0, 3.0);
List<? extends Number> list2 = wildUnmodifiableList(src);
int freq2 = frequency(list2, 0.0); // COMPILE-TIME ERROR
The reason is that the declaration of frequency
requires the second argument to be a subtype of the element type of the first argument. In the first case it's a Double
which is a subtype of Number
. But in the second case, Double
is not a subtype of ? extends Number
, resulting in a type mismatch.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With