8

Recently some people said that I was wrong when I explained why one cannot insert a Number object into a list declared as List<? super RationalNumber>. (This assumes that RationalNumber is a subclass of Number)

My explanation was that in List<? super RationalNumber> the <? super RationalNumber> stands for an unknown super class of RationalNumber. This means that <? super RationalNumber> indicates that a valid object for List<? super RationalNumber> is an object that extends an unknown super class of RationalNumber.

Since an unknown super class of RationalNumber is really unknown (e.g. it can be an interface X instead of Number), there is not guarantee that Number is a sub-class of X i.e. implements X, so Number is not a valid substitution of <? super RationalNumber>.

What is wrong with this explanation? Edit: Note that I am asking for what wrong with the way I explain, my explanation. Not another explanation for the original question.

Thanks.

3
  • @rwong Thanks, I know how people explain it. I don't understand what is wrong with my explanation and how I explain it.
    – InformedA
    Commented Jul 4, 2014 at 7:12
  • everything I said is in the context of a List. I will edit my question
    – InformedA
    Commented Jul 4, 2014 at 7:27
  • I think I get what you mean now. I will try to see things in the same way of the link you post.
    – InformedA
    Commented Jul 4, 2014 at 7:37

2 Answers 2

7

List<? super RationalNumber> is a list of some type X that is a superclass of RationalNumber. Your assumption is that RationalNumber is a subclass of Number, so the possible candidates for X are RationalNumber, Number, or Object. The most restrictive possibility is that X is RationalNumber itself. A List<RationalNumber> can contain only instances of RationalNumber, and cannot contain instances of Number.

In short, a List<? super T> is a list that can accept instances of T, but the current contents are of unknown type. This contrasts with List<? extends T>, which contains instances of T, but does not allow adding new elements because the type accepted is unknown.

This example illustrates the use of super:

void storePies(List<? super RationalNumber> list) { 
    list.add(new RationalNumber(22, 7))
    list.add(new RationalNumber(355, 113))
}

...
List<Number> list = new ArrayList<Number>();
storePies(list);
list.add(3.14159265358979);

This example illustrates the use of extends:

double sum(Collection<? extends Number> numbers) {
    double s = 0;
    for (Number n: numbers) { s += n.doubleValue(); }
    return s;
}

List<Integer> somePrimes = new ArrayList<Integer>();
somePrimes.add(5);
somePrimes.add(17);
double total = sum(somePrimes);
1
  • Thanks, but I have just edited my question for better clarification.
    – InformedA
    Commented Jul 4, 2014 at 7:19
5

See also:


A digression. Feel free to skip.

For programmers who are also familiar with C#, a similar feature exists:

The terms, covariance and contravariance, can be traced back to the early discussions coming from the Liskov substitution principle (LSP).


The design goal of lower-bounded wildcard (LBWC) is to ensure the type safety of the following piece of code.

(This is just one example; one of the very simple example among others.)

List<? super RationalNumber> containerOne = new ArrayList<RationalNumber>(); 
List<? super RationalNumber> containerTwo = new ArrayList<Number>(); 
List<? super RationalNumber> containerThree = new ArrayList<Object>(); 

RationalNumber itemValue = new RationalNumber(22, 7); 

containerOne.add(itemValue); 
containerTwo.add(itemValue); 
containerThree.add(itemValue);

In short, LBWC allows the "handles" - generic classes having an LBWC (containerOne, containerTwo, containerThree in the above sample) to:

  • Be assigned instances of the generic class (of type parameter T) whenever it is known that RationalNumber can be safely up-cast to T.

Now, we use this knowledge and try to answer the question: can we pass in a Number to such a "handle" - a generic class having an LBWC of RationalNumber?


Given that

  • It is valid to assign an instance of new List<RationalNumber>() to a handle of List<? super RationalNumber> varGeneric.
  • Of course it is valid to do something entirely different; but the design of LBWC is that this is the situation that it needs to deal with.

We obtain this corollary question:

  • Can we call new List<RationalNumber>().add(new Number(...)) ?
    • Of course not.

Seeing that it is LBWC's design goal to prevent this from happening, one can derive this conclusion:

  • If LBWC is designed to be enforceable at all (by the compiler, or language police), it must prevent this assignment (passing in a Number for a type ? for which the LBWC is RationalNumber) from happening.

This answers the question from a design perspective. The matter of enforcement can only be answered by senior members of the community who are familiar with the construction of the Java language and its compiler tools. Comments from the general crowd will only provide speculations, or "hypotheses" or "theories".

0

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