6

I am reading the Core Java (9th edition) by Cay S. Horstmann and Gary Cornell. After making an effort, I cannot still understand the ? super Manager. Here are some materials relating to this question.

public class Pair<T> {
    private T first;
    private T second;

    public Pair(T first, T second) {
        this.first = first;
        this.second = second;
    }

    public void setFirst(T first) {
        this.first = first;
    }

    public void setSecond(T second) {
        this.second = second;
    }

    public T getFrist() {
        return this.first;
    }

    public T getSecond() {
        return this.second;
    }
}

The Manager inherits from Employee, and Executive inherites from Manager. Pair has methods as follows:

void setFirst(? super Manager)
? super Manager getFirst()

Since ? super Manager denotes any supertype of Manager, why I cannot call setFirst method with Employee (it is obvious that Employee is the supertype of Manager), but only with type Manager or a subtype such as Executive(but Executive is the subtype of Manager, not a supertype of Manager)?

1

2 Answers 2

3

One can read and re-iterate it over and over again - as it has been many times already. In a comment to your question, Philipp already linked to a question on Stackoverflow which tries to answer it. For me, the explanation of Uncle Bob did the trick.

He writes (next to a lot of other good stuff):

A List<? super Shape> is a list into which you may add Shapes. Another way to think of this is that the type <? super T> is an unknown type that T can substitute for. You may have to read that several times before you understand it. I certainly did. So here's yet another way to think about it. The type <? super T> is a type that T is derived from. It is a base class of T. Clearly if D is derived from B then you can put D instances into a List<B>. Likewise, since T derives from <? super T>, you can put T instances into a List<? super T>. Follow? No? Neither did I for awhile. It takes a bit of getting used to.

What this means in the end is that your assumption you'd be able to put super-types of Manager into your List<? super Manager> is wrong. That's not what is meant here. The super keyword solves the problem of not being able to insert stuff into generic places while you were able to get some out (with extends).

Imagine two pairs, one that extends Manager, one that "supers" it:

Pair<? extends Manager> subPair = new Pair<Manager>();
Pair<? super Manager> superPair = new Pair<Manager>();

What you can never do, is insert an Employee in any of these:

subPair.setFirst(new Employee());
superPair.setFirst(new Employee());

If you want managers, employees will never be enough. And it's worse: you can't even insert an Executive into your subPair, even though Executive extends Manager. It won't compile either:

subPair.setFirst(new Executive());

Edit: Why is that? Well, your generic subPair always refers to some "explicit" Pair, Pair<Manager> in this case. It just as well might have referred to a Pair<CoManager> (with CoManager extends Manager). Java won't know the explicit type that's actually used and thus can't allow you insertions.

But super comes to the rescue. With super you can insert Managers and their sub-types into your Pair. And that is what super is for: Inserting.

superPair.setFirst(new Executive());

Edit: Any why does that work? While extends guarantees you some type that implements (or derives from) Manager, super guarantees you any such type. So, while with extends you get a specific type with (at least) the Manager interface, with super you can insert any type with the Manager interface.

And for the bigger picture, I really suggest reading the article of Uncle Bob.

4
  • "you can't even insert an Executive into your subPair, even though Executive extends Manager." Why not?
    – Brandon
    Jul 19, 2014 at 15:04
  • @Brandon With extends the compiler guarantees that subPair will be a Pair with some kind of objects deriving from Manager. It could very well be a Pair of CoManager and you cannot insert an Executive into a Pair<CoManager>. super on the other hand guarantees that superPair always is a Pair with elements that at least implement Manager. You can thus insert any kind of Manager. Admittedly, Java is a bit weird with this...
    – jhr
    Jul 21, 2014 at 5:05
  • What is the difference between deriving from Manager and implementing Manager? In an OOP world, those feel like synonyms to me. I have been programming in Java professionally for 6 years now and still do not understand the difference between super and extends.
    – Brandon
    Jul 21, 2014 at 11:22
  • @Brandon I just edited my answer. You're right, deriving from something and implementing it, in an OOP world, basically is the same, at least for the purpose here. The trick lies more between some and any. I hope my addition helps.
    – jhr
    Jul 24, 2014 at 7:02
0

The difference seems to be the purpose of your variable.

If you use a variable of type Pair to read then you use Pair<? extends Manager> In this case, when calling a method of pair that returns something parameterized, you will have the benefit of type-safe. But you almost cannot write anything into it, always a compilation error.

On the other hand, if you use a variable of type Pair to write then you use Pair<? super Manager>. In this case, when calling a method of Pair<> type that receives a parametrized variable of type (or subtype) of Manager, you have the type safe.

Honestly, I don't see the difference between Pair<? super Manager> and Pair<Manager> except that when you use Pair<? super Manager> the read methods in Pair<> returns generic java Object (ie not type safe for read, the compiler kind of discourages reading in this case)

I think seeing it as read and write like this is easier.

Now back to the why you cannot write an Employee object into your Pair<? super Manager>. This is because <? super Manager> is actually a type that extends some unknown supertype of Manager. And because it is unknown, you don't know if Employee actually is a subtype of that unknown type.

Even more concrete: let's say Manager also implements an interface IManager. Then <? super Manager> can be an IManager object and now Employee is not a valid substitution of IManager (because it is not a subtype of IManager). In this case, any subtype of Manager can be a substitution because it will be subtype of any unknown supertype of Manager. You can call setFirst(Executive e) here.

Not the answer you're looking for? Browse other questions tagged or ask your own question.