Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Java generics super keyword

Tags:

java

generics

I went through these topics

  • Generics..? Super T
  • Bounding generics with 'super' keyword

However, I still seem to be kind of lost with super keyword:

  1. When we declare a collection like that:

    List<? super Number> list = null; list.add(new Integer(0)); // this compiles list.add(new Object()); // this doesn't compile 

shouldn't it be the opposite - we have a list that contains some objects (of unknown type) which are parents of Number. So Object should fit (since it is the parent of Number), and Integer shouldn't. The opposite is the case for some reason.

  1. Provided we have the following code

    static void test(List<? super Number> param) {   param.add(new Integer(2)); }  public static void main(String[] args) {   List<String> sList = new ArrayList<String>();   test(sList);            // will never compile, however... } 

It is impossible to compile the above code (and my sanity suggests that this is the right behaviour), but the basic logic could prove the opposite:

String is Object, Object is superclass of Number. So String should work.

I know this is crazy but isn't this the reason why they didn't allow <S super T> constructs? If yes, then why <? super T> is allowed?

Could someone help me restore the missing part of this logic chain?

like image 287
Denis Kniazhev Avatar asked Oct 02 '10 19:10

Denis Kniazhev


People also ask

What is super and extends in generics in Java?

super is a lower bound, and extends is an upper bound.

What is <? Super T in Java?

super T denotes an unknown type that is a supertype of T (or T itself; remember that the supertype relation is reflexive). It is the dual of the bounded wildcards we've been using, where we use ? extends T to denote an unknown type that is a subtype of T .

What does super () do in Java?

The super() in Java is a reference variable that is used to refer parent class constructors. super can be used to call parent class' variables and methods. super() can be used to call parent class' constructors only.

Can I use polymorphism in generics?

The polymorphism applies only to the 'base' type (type of the collection class) and NOT to the generics type.


2 Answers

The bounded wildcard in List<? super Number> can capture Number and any of its supertypes. Since Number extends Object implements Serializable, this means that the only types that are currently capture-convertible by List<? super Number> are:

  • List<Number>
  • List<Object>
  • List<Serializable>

Note that you can add(Integer.valueOf(0)) to any of the above types. however, you CAN'T add(new Object()) to a List<Number> or a List<Serializable>, since that violates the generic type safety rule.

Hence it is NOT true that you can add any supertype of Number to a List<? super Number>; that's simply not how bounded wildcard and capture conversion work. You don't declare a List<? super Number> because you may want to add an Object to it (you can't!); you do because you want to add Number objects to it (i.e. it's a "consumer" of Number), and simply a List<Number> is too restrictive.

References

  • Angelika Langer's Generics FAQs
    • What is a bounded wildcard?
    • When would I use a wildcard parameterized type with a lower bound? ("When a concrete parameterized type would be too restrictive.")
    • Why is there no lower bound for type parameters? ("Because it does not make sense.")
  • JLS 5.1.10 Capture Conversion

See also

  • Effective Java 2nd Edition, Item 28: Use bounded wildcards to increase API flexibility
    • "PECS stands for producer-extends, consumer-super

Related questions

  • Too many to list, PECS, new Integer(0) vs valueOf, etc
like image 75
polygenelubricants Avatar answered Sep 19 '22 19:09

polygenelubricants


For the first part List<Number> fits in List<? super Number> but you can't add an Object to a List<Number>. That's why you can't add an Object to List<? super Number>.

On the other hand you can add every subclass of Number (Number included) to your list.

For the second part, String is an Object, but String isn't a superclass of Number.

If it worked like this, as every class is a subclass of Object, super would have no meaning.


Let's see every possible cases with List<? super Number> :


  • The passed list is a List<Object>
    • List<Object> will work
    • Object fits in <? super Number>
    • You can add any subtype of Number to a List<Object>
    • Even if you could also add String in it the only thing you're sure of is that you can add any subclass of Number.

  • The passed list is a List<Number> :
    • List<Number> will work
    • Number fits in <? super Number>
    • You can add any subtype of Number to a List<Number>

  • The passed list is a List<Integer> (or any subclass of Number):
    • List<Integer> won't work
    • Integer is a subclass of Number so it is exactly what we want to avoid
    • Even if an Integer fits in a Number you wouldn't be abble to add any subclass of Number in a List<Integer> (for example a Float)
    • super doesn't mean a subclass.

  • The passed list is a List<String> (or any class not extending Number nor in the "super hierarchy" of Number (ie. Number and Object) :
    • List<String> won't work
    • String doesn't fit in Number "super hierarchy"
    • Even if String fits in Object (which is a super class of Number) you woudln't be sure to be able to add a Number to a List that contain any subclass from one of the super classes of Number)
    • super doesn't mean any subclass of one of the super classes, it only means one of the super classes.

How does it work ?

You could say that as long as you can add any subclass of Number with your typed List, it respects the super keyword.

like image 45
Colin Hebert Avatar answered Sep 18 '22 19:09

Colin Hebert