Reputation: 1114
Joshua Bloch came up with the PECS, which says the rule when to use ? extends T
and ? super T
. If you think about PECS in terms of Collections framework, then it is very straightforward. If you add values to the data structure, use ? super T
. If you read from the data structure, use ? extends T
.
for instance:
public class Collections {
public static <T> void copy(List<? super T> dest, List<? extends T> src) {
for (int i = 0; i < src.size(); i++)
dest.set(i, src.get(i));
}
}
If I check the signature of
public static <T> void sort(List<T> list, Comparator<? super T> c)
I see Comparator uses ? super
, so it should be a consumer. Looking at the code, comparator c is only used to produce stuff because it is asked the logic of comparing.
On one hand, I understand why it is a super because as a developer I want to use comparators of class T
and also comparators of super class T
because objects of T
is also of type super classes of T
. But when I try to think in terms of PECS, I fail to understand.
Does PECS only hold for Collections framework? If not, Can someone explain to me what does comparator consume in Collections.sort
?
Upvotes: 2
Views: 403
Reputation: 34470
For the sake of this answer, let's take Comparator
as the main, guiding example.
If you think carefully about it, you'll see that Comparator
actually receives two arguments of type T
and returns the result of their comparison (represented by an int
). In other words, it consumes two instances of type T
and produces an int
value. So, as by the PECS rule, it is a consumer of T
, hence the use of ? super T
.
More generally, you should consider producer and consumer from the point of view of the main type with regard to the types of each one of its generic parameters. If some Comparator
type consumes objects of type T
, the PECS rule states that users of such Comparator<T>
could use it to compare objects whose type is a subtype of T
.
As a specific example, if you happen to already have the logic to compare two generic Number
instances (no matter what their concrete type actually is), you could use it i.e. to compare Double
instances, because doubles are numbers, after all.
Consider the following comparator:
Comparator<Number> c = Comparator.comparingInt(Number::intValue);
Here, the comparator c
compares Number
instances (any number) by taking into consideration only their integral part.
If you have the following list of Double
instances:
List<Double> doubles = Arrays.asList(2.2, 2.1, 7.3, 0.2, 8.4, 9.5, 3.8);
And the following sort
method:
static <T> void sort(List<T> list, Comparator<T> c) {
list.sort(c);
}
(Note the absence of the wildcard ? super T
in the Comparator
argument).
Then, if you want to sort the List<Double> doubles
list, the signature of the above sort
method would require you to pass a concrete Comparator<Double>
. But what if you wanted to use your previously defined c
comparator to sort the List<Double> doubles
?
As that comparator's type is Comparator<Number>
, and as the doubles
list's type is List<Double>
, the following code would produce a compilation error:
sort(doubles, c);
Fortunately, as Comparator
is a consumer of the type of the elements it compares, you can change the signature of the sort
method to:
static <T> void sort(List<T> list, Comparator<? super T> c) {
list.sort(c);
}
And now, this code would compile:
sort(doubles, c);
Upvotes: 4