-1

I am studying Java Generics using the book "Introduction to Java Programming and Data Structures" by Y. Daniel Liang, and I don't understand one of the exercises:

"19.3 (Pair of objects of the same type) Create a Pair class that encapsulates two objects of the same data type in an instance of Pair.

19.4 (Using wildcards) Write a generic static method that returns the smallest value in an instance of Pair from Programming Exercise 19.3."

The solution code is as follows:


public class Exercise19_04 {

    public static class Pair<T> {
        public final T one;
        public final T two;

        public Pair(T one, T two) {
            this.one = one;
            this.two = two;
        }
    }
    
    public static class Main {
        // To have the smallest between two objects, they need to be comparable.
        public static <T extends Comparable<? super T>> T smallest(Pair<T> p) {
            if (p.one.compareTo(p.two) < 0) {
                return p.one;
            } else {
                return p.two;
            }
        }
        
        public static void main(String[] args) {
            Pair<Double> pd = new Pair<>(7.0, 6.3);
            System.out.println(smallest(pd));
            Pair<String> ps = new Pair<>("big", "small");
            System.out.println(smallest(ps));
            /* Lines below are not compilable because Color is not comparable
            Pair<String> pc = new Pair<>(java.awt.Color.BLUE, java.awt.Color.RED);
            System.out.println(smallest(ps));*/
        }
    }
}

My problem is with the method declaration public static <T extends Comparable<? super T>> T smallest(Pair<T> p)

I understand that in order to compare the objects T within an instance of Pair, T must be an instance of Comparable and therefore you have to declare <T extends Comparable>. However I don't understand the <? super T> part thereafter. Can somebody explain this to me? Thanks in advance!

  • 1
    Does this answer your question? [What is PECS (Producer Extends Consumer Super)?](https://stackoverflow.com/questions/2723397/what-is-pecs-producer-extends-consumer-super) – Turing85 Dec 29 '21 at 19:42
  • https://docs.oracle.com/javase/tutorial/java/generics/subtyping.html – Robert Harvey Dec 29 '21 at 19:42
  • 1
    To the closer-voters: OP clearly isn't struggling with PECS, nor with specifically why ` super T>` instead of just ``; op simply wants an explanation for why Comparable even has generics. – rzwitserloot Dec 29 '21 at 19:45
  • How is it that these close votes are not registering? – Robert Harvey Dec 29 '21 at 19:48
  • @RobertHarvey The close votes registered, but the question has been reopened now. – cigien Dec 29 '21 at 19:51
  • @rzwitserloot I do not see any indication that this is the actual question. As-is, the question is then at least unclear. – Turing85 Dec 29 '21 at 19:52
  • OP literally says: ``, in code quotes and everything, @Turing85. Note, not `>`, hence not an 'exact duplicate'. Barely the same ballpark. – rzwitserloot Dec 29 '21 at 21:37
  • @rzwitserloot For me, it's unclear whether OP does not understand why `Comparable` is generic or why we use `? super T`, hence for me the question is unclear. But you answer covers both, so... it doesn't matter. – Turing85 Dec 30 '21 at 06:06

1 Answers1

2

Forget about Pair for a moment. Let's just look at Comparable. Initially one might think Comparable needs no parameters. But there's a problem in that. What does the signature of compareTo (as in a.compareTo(b)) look like?

Perhaps:

/**
 * Return negative if a is 'before' b, positive if a is 'after' b,
 * and 0 if they are equal in ranking.
 */
 public int compareTo(Object other);
}

But this is problematic: Now for e.g. Double to implement Comparable, it needs to be capable of answering that question for any type. so if I try to compare a Double with value 4.5 against, say, an InputStream - that's a nonsensical question. Asking to compare guns and grandmas.

So, we'd strongly prefer for the signature to be:

/**
 * Return negative if a is 'before' b, positive if a is 'after' b,
 * and 0 if they are equal in ranking.
 */
 public int compareTo(Double other);
}

But how? We can't make a new Comparator interface for every type in existence, and there is nothing in java that means 'your own type'.

Generics to the rescue! Let's make a generics parameter that represents the type against which you can compare yourself:

public interface Comparable<T> {
  public int compareTo(T other);

And let's implement it in double:

public class Double extends Number implements Comparable<Double> {
 ...

We're going to read the type Comparable as: "Comparables of foos", which means: Things which can be compared against things that have the Foo type. (And generally, we just mean Foos with that; we are interested in comparing oranges to oranges, of course).

Now lets stick it back into Pair. We can't write Pair<Comparable> for the same reason you can't write List<List>. Yes, it'll compile, but you get severe warnings: After all, okay a list of lists. But what are the lists in the lists listing? You'd write List<List<String>>: A list of lists of strings. Same applies to comparables: It's not just a Pair of Comparables, it's a Pair of Comparables of Foos. A Pair<Comparable<String>> means: I have a pair of 2 things, each of which is of some type that knows how to compare itself to strings. (And, spoiler alert, only java.lang.String fits that bill).

That juust leaves 2 problems:

  1. Why is it Pair<? extends Comparable<..>> and not just Pair<Comparable>
  2. Why is it Pair<... Comparable<? super T>> and not just Comparable<T>?

The answers to these:

  1. Because of PECS. See the linked answer in the comments.
  2. This is rarely relevant, but in theory, if you have some object of a type that is capable of comparing itself to some SUPERtype of strings (let's say 'can compare itself to any object of any type'), well, that'd.. be just fine. We want anything that knows what to do when I feed it: Hey, compare yourself to this thingie, and the thingie is of type T. Whatever that might be. If the thingie can compare itself to some supertype of T, that's totally fine. All Integers are also Numbers - all Ts are also any supertype of T.
rzwitserloot
  • 85,357
  • 5
  • 51
  • 72