fge
fge

Reputation: 121790

Java, generics and PECS: still having trouble understanding the C part; concrete example?

I'll post a single link here: Collections.sort(). There have been many posts on SO with regards to the PECS paradigm, including this one. In my own personal code, I use generics quite a lot, but have only ever had the use of the P part (that is, <X extends SomethingElse>).

Collections.sort expects as its generics argument a <T extends Comparable<? super T>>. I fail to see where the super kicks in in there. Do you have a concrete example of why this is necessary?

While I am at it, I am deeply afraid that I don't understand the full implications of P either... I have read many, many links, without having had a clear, obvious proof that P is P and C is C...

EDIT As to the may already have an answer here": no, sorry. I have read this link, and many others on SO. This still does not tell me the core mechanism behind it all. The two answers given to me so far give me hints, in fact more so than all the links I could find so far.

Upvotes: 1

Views: 595

Answers (4)

Xavier Z
Xavier Z

Reputation: 402

Let me try to illustrate this question by sharing my shallow idea.

For PE and CS, the easier part is PE: a producer with Extends.
The logic is, we can get an object from the producer, and the object extends a certain type (say type A) so that it is guaranteed that this object can behave like type A.

To intuitively understand the harder part, i.e., consumer with super, we need to recall Subtype Polymorphism meaning a more generic reference can hold an object with its descendants type.

Now, the situation is, we want a collection to collect what we give, i.e. the collection is the consumer.
To safely put our product, we need to know what is the most generic type that can be held in the collection, so that we can adapt the type of our product.
And the super keyword satifies our needs.

Say a List< ? super Animal> means the list can hold anything is-a Animal
(the list could be for a more generic type, but we just do not care),
and that is a clear requirement for our production process.

On the contrary, with the statement List<? extends Animal>, you are not guaranteed to put any type into the list, as the list might be for any specific type extending the animal.
That is why the consumer needs to specify with a super, as I thought.


It is helpful to understand the concept with specific context.
For example the Collections.sort signature as follows.

public static <T extends Comparable<? super T>> void sort(List<T> list) {
        list.sort(null);
}

The intuition is, to apply the sorting, the object list should satisfies certain criterion: at least each element can be compared to a object of the same type.
The question is, how can we describe the requirement above with "generic symbols" ?

Let us split the generic declaration.

<T extends Comparable<? super T>>, the first part T extends means T must implement a Comparable, and the second part Comparable<? super T> specifying the type T can compare to.

Specifically, ? super T above means T can at least compare to the object of the same type.
The reality might be that, T had implemented more genric Comparable<Object>.
But it is enough for sort: T can be compared to itself.

Likewise, if we define the generic as
<T extends Comparable<? extends T>>
Then it is not guaranteed to apply the sort: the element T could be only able to compare to objects with descendant type rather than itself, while some elements in the list could be of type T.
At this circumstances, the sort will fail for the target list.

Upvotes: 0

newacct
newacct

Reputation: 122489

I fail to see where the super kicks in in there. Do you have a concrete example of why this is necessary?

Say you have a class Animal, which is comparable to all Animals:

public class Animal implements Comparable<Animal>

Now let's say you have a class Dog, which extends Animal:

public class Dog extends Animal

Now the question is, can Dog compare to another Dog? (i.e. can you do myDog.compareTo(yourDog)) The answer is of course. A Dog, as an Animal, is comparable to all Animals, including Dogs (as well as Cats).

However, Dog does not implement Comparable<Dog>, so a bound T extends Comparable<T> would not work for T = Dog.

But for sorting all we care is that the type can compare to itself (which Dog can do). So the suitable least restrictive bound is T extends Comparable<? super T>, which does work for Dog.

Upvotes: 2

Alexis C.
Alexis C.

Reputation: 93862

Try to create a List of Number :

List<Number> list = new ArrayList<Number>(Arrays.asList(2, 3.14, 5, 0, 42, 2.5));

Now try to sort it :

Collections.sort(list);

Looking at the generic argument <T extends Comparable<? super T>>, it equivalent in this case to <Number extends Comparable<? super Number>>. However the Number class doesn't implement such a comparator. Hence the code does'nt compile and it forces you to create your own one.

Upvotes: 1

Puce
Puce

Reputation: 38142

E.g.

List<Interger> myIntegers = Arrays.asList(5, 2, 3);

List<Long> myLongs = Arrays.asList(5L, 2L, 3L);

MyNumberComparator myNumberComparator = new Comparator<Number>(){...}

Collections.sort(myIntegers, myNumberComparator ); // Number is "super" class of Integer
Collections.sort(myLongs , myNumberComparator ); // Number is "super" class of Long

So "super" here allows to reuse MyNumberComparator for both sorting Integers and sorting Longs.

Upvotes: 2

Related Questions