76

I understand that <? super T> represents any super class of T (parent class of T of any level). But I really struggle to imagine any real life example for this generic bound wildcard.

I understand what <? super T> means and I have seen this method:

public class Collections {
  public static <T> void copy(List<? super T> dest, List<? extends T> src) {
      for (int i = 0; i < src.size(); i++)
        dest.set(i, src.get(i));
  }
}

I am looking for an example of real life use case where this construction can be used and not for an explanation of what it is.

Michał Turczyn
  • 28,428
  • 14
  • 36
  • 58
BanzaiTokyo
  • 1,208
  • 1
  • 16
  • 26
  • this should give you an idea of when to use super: https://stackoverflow.com/questions/4343202/difference-between-super-t-and-extends-t-in-java – ItFreak Sep 05 '18 at 13:10
  • 18
    this is NOT a duplicate, a very valid question – Eugene Sep 05 '18 at 13:12
  • 6
    i also dont think its a duplicate, he is asking for concrete situations, not the principle behind – ItFreak Sep 05 '18 at 13:28
  • 2
    This is the start of the answer that I was about to write when this one was closed: I agree to some extent with the close-voters: The answer *could* be derived, with some diligence, from https://stackoverflow.com/questions/2723397/what-is-pecs-producer-extends-consumer-super . However, this question (as well as the answers here) focus on the technical principle. A simple, **realistic** example of where it makes sense to use `? super T` could be helpful. – Marco13 Sep 05 '18 at 14:49
  • 4
    Don't think this should have been closed as a duplicate, since the author is asking for real-world models of OOP, rather than in-depth explanations of how inheritance works in Java. – John Stark Sep 05 '18 at 14:49
  • Check out the [Variance section of Kotlin tutorial](https://kotlinlang.org/docs/reference/generics.html#variance). It gives a couple of examples and why Kotlin decided to use `in`/`out` instead of `? super T` and `? extends T`. – Bakuriu Sep 05 '18 at 17:15
  • 1
    Possible duplicate of [Why is T bounded by Object in the Collections.max() signature?](https://stackoverflow.com/questions/19488357/why-is-t-bounded-by-object-in-the-collections-max-signature) – Benjamin Gruenbaum Sep 05 '18 at 17:20
  • I give an extensive discussion of co-, contra-, and invariance in this answer: https://stackoverflow.com/a/50303536/2988 The question is framed as a Scala question, but my answer discusses variance in full generality without referencing any particular language. – Jörg W Mittag Sep 05 '18 at 21:01
  • By the way, it is quite normal to be confused by this. Subtyping is easy. Parametric Polymorphism (Generics) is easy. Somehow, when you mix the two, everybody gets confused. Eiffel, a language that is *specifically designed* as a "safe" programming language, originally got this wrong, and Bertrand Meyer is a *very* smart guy. Java arrays are completely broken in this respect. Even worse: C♯ arrays, even though the designers had the benefit of learning from Java, are completely broken. Generics in Dart are *deliberately* broken to make them easier to understand. – Jörg W Mittag Sep 05 '18 at 21:21
  • Note: the real-life examples are at the end of the answer I mentioned in my [previous comment](https://stackoverflow.com/questions/52185915/what-is-a-real-life-example-of-generic-super-t#comment91336404_52185915). – Jörg W Mittag Sep 05 '18 at 21:23
  • 3
    Isn't this example here a real life use case? – user253751 Sep 06 '18 at 01:21

9 Answers9

50

The easiest example I can think of is:

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

taken from the same Collections. This way a Dog can implement Comparable<Animal> and if Animal already implements that, Dog does not have to do anything.

EDIT for a real example:

After some email ping-pongs, I am allowed to present a real example from my work-place (yay!).

We have an interface called Sink (it does not matter what it does), the idea is that is accumulates things. The declaration is pretty trivial (simplified):

interface Sink<T> {
    void accumulate(T t);
}

Obviously there is a helper method that takes a List and drains it's elements to a Sink (it's a bit more complicated, but to make it simple):

public static <T> void drainToSink(List<T> collection, Sink<T> sink) {
    collection.forEach(sink::accumulate);
}

This is simple right? Well...

I can have a List<String>, but I want to drain it to a Sink<Object> - this is a fairly common thing to do for us; but this will fail:

Sink<Object> sink = null;
List<String> strings = List.of("abc");
drainToSink(strings, sink);

For this to work we need to change the declaration to:

public static <T> void drainToSink(List<T> collection, Sink<? super T> sink) {
    ....
}
Eugene
  • 102,901
  • 10
  • 149
  • 252
  • 1
    Kotlin [tutorial](https://kotlinlang.org/docs/reference/generics.html#declaration-site-variance) uses `Source` as example... which is basically the dual of your `Sink` example. – Bakuriu Sep 05 '18 at 17:16
  • 2
    Surely you could define it in the opposite way by making the list ? extends T? – Weckar E. Sep 05 '18 at 17:46
  • @WeckarE. Not if the method is on the list itself. – Reinstate Monica Sep 06 '18 at 01:35
  • 1
    @WeckarE. I could yes, but this would change the semantics a bit now I can't add anything to that `list`, it becomes a producer only; as said this is a simplified example... – Eugene Sep 10 '18 at 09:14
16

Suppose you have this class hierarchy: Cat inherits from Mammal, which in turn inherits from Animal.

List<Animal> animals = new ArrayList<>();
List<Mammal> mammals = new ArrayList<>();
List<Cat> cats = ...

These calls are valid:

Collections.copy(animals, mammals); // all mammals are animals
Collections.copy(mammals, cats);    // all cats are mammals
Collections.copy(animals, cats);    // all cats are animals
Collections.copy(cats, cats);       // all cats are cats 

But these calls are not valid:

Collections.copy(mammals, animals); // not all animals are mammals
Collections.copy(cats, mammals);    // not all mammals are cats
Collections.copy(cats, animals);    // mot all animals are cats

So the method signature simply insures that you copy from a more specific (lower in the inheritance hierarchy) class to a more generic class (upper in the inheritance hierarchy), and not the other way round.

Benoit
  • 4,340
  • 2
  • 16
  • 36
  • 2
    The `super` keyword isn't necessary for this to work, though. This signature would also have exposed identical behavior: `public static void copy(List dest, List extends T> src) {` – Nick Sep 06 '18 at 11:41
  • 1
    @Nick Good catch. Why this signature then ? This question should be raised to Java language designers. So far the only reason I found is that you can write code like: `Collections.copy(animals, cats);` - but I don't know why someone would/should write code like this... – Benoit Sep 06 '18 at 13:14
6

For example, look into the Collections.addAll method implmenetation:

public static <T> boolean addAll(Collection<? super T> c, T... elements) {
    boolean result = false;
    for (T element : elements)
        result |= c.add(element);
    return result;
}

Here, the elements can be inserted into any collection whose element type is a supertype of the type T of the element.

Without a lower bounded wildcard:

public static <T> boolean addAll(Collection<T> c, T... elements) { ... }

the following would have been invalid:

List<Number> nums = new ArrayList<>();
Collections.<Integer>addAll(nums , 1, 2, 3);

because the term Collection<T> is more restrictive than Collection<? super T>.


Another example:

Predicate<T> interface in Java, that uses a <? super T> wildcard in the following methods:

default Predicate<T> and(Predicate<? super T> other);

default Predicate<T>  or(Predicate<? super T> other);

<? super T> allows to chain a wider range of different predicates, for example:

Predicate<String> p1 = s -> s.equals("P");
Predicate<Object> p2 = o -> o.equals("P");

p1.and(p2).test("P"); // which wouldn't be possible with a Predicate<T> as a parameter
Oleksandr Pyrohov
  • 13,194
  • 4
  • 51
  • 85
  • I don't think this example is particularly compelling. If you leave out the explicit instantiation of the type variable, you're just as able to write `Collections.addAll(nums, 1, 2, 3)` with either method signature. – Nick Sep 07 '18 at 01:55
2

Suppose you have a method:

passToConsumer(Consumer<? super SubType> consumer)

then you call this method with any Consumer which can consume SubType:

passToConsumer(Consumer<SuperType> superTypeConsumer)
passToConsumer(Consumer<SubType> subTypeConsumer)
passToConsumer(Consumer<Object> rootConsumer)

For exmaple:

class Animal{}

class Dog extends Animal{

    void putInto(List<? super Dog> list) {
        list.add(this);
    }
}

So I can put the Dog into List<Animal> or List<Dog>:

List<Animal> animals = new ArrayList<>();
List<Dog> dogs = new ArrayList<>();

Dog dog = new Dog();
dog.putInto(dogs);  // OK
dog.putInto(animals);   // OK

If you change putInto(List<? super Dog> list) method to putInto(List<Animal> list):

Dog dog = new Dog();

List<Dog> dogs = new ArrayList<>();
dog.putInto(dogs);  // compile error, List<Dog> is not sub type of List<Animal>

or putInto(List<Dog> list):

Dog dog = new Dog();

List<Animal> animals = new ArrayList<>();
dog.putInto(animals); // compile error, List<Animal> is not sub type of List<Dog>
xingbin
  • 23,890
  • 7
  • 43
  • 79
  • 1
    whenever you say `Consumer` or `consume` this automatically falls into the `PECS` category, not saying this is bad though, good examples – Eugene Sep 05 '18 at 14:57
1

I wrote a webradio, so I had the class MetaInformationObject, which was the superclass for PLS and M3U playlists. I had a selection dialogue, so I had:

public class SelectMultipleStreamDialog <T extends MetaInformationObject>
public class M3UInfo extends MetaInformationObject
public class PLSInfo extends MetaInformationObject

This class had a method public T getSelectedStream().
So the caller received a T which was of the concrete type (PLS or M3U), but needed to work on the superclass, so there was a list: List<T super MetaInformationObject>. where the result was added.
Thats how a generic dialogue could handle the concrete implementations and the rest of the code could work on the superclass.
Hope that makes it a little more clearer.

ItFreak
  • 1,986
  • 4
  • 17
  • 36
1

Consider this simple example:

List<Number> nums = Arrays.asList(3, 1.2, 4L);
Comparator<Object> numbersByDouble = Comparator.comparing(Object::toString);
nums.sort(numbersByDouble);

Hopefully this is a somewhat compelling case: You could imagine wanting to sort the numbers for display purposes (for which the toString is a reasonable ordering), but Number is not itself Comparable.

This compiles because integers::sort takes a Comparator<? super E>. If it took just a Comparator<E> (where E in this case is Number), then the code would fail to compile because Comparator<Object> is not a subtype of Comparator<Number> (due to reasons that your question indicates you already understand, so I won't go into).

yshavit
  • 39,951
  • 7
  • 75
  • 114
1

Collections serve as a good example here.

As stated in 1, List<? super T> lets you create List that will hold elements of type, that are less derived than T, so it can hold elements that inherit from T, that are type of T and that T inherits from.

On the other hand, List<? extends T> lets you define a List that can hold only elements that inherit from T (in some cases not even of type T).

This is a good example:

public class Collections {
  public static <T> void copy(List<? super T> dest, List<? extends T> src) {
      for (int i = 0; i < src.size(); i++)
        dest.set(i, src.get(i));
  }
}

Here you want to project List of less derived type to List of less derived type. Here List<? super T> assures us that all elements from src will be valid in the new collection.

1 : Difference between <? super T> and <? extends T> in Java

Peter Mortensen
  • 28,342
  • 21
  • 95
  • 123
Michał Turczyn
  • 28,428
  • 14
  • 36
  • 58
0

Say you have:

class T {}
class Decoder<T>
class Encoder<T>

byte[] encode(T object, Encoder<? super T> encoder);    // encode objects of type T
T decode(byte[] stream, Decoder<? extends T> decoder);  // decode a byte stream into a type T

And then:

class U extends T {}
Decoder<U> decoderOfU;
decode(stream, decoderOfU);     // you need something that can decode into T, I give you a decoder of U, you'll get U instances back

Encoder<Object> encoderOfObject;
encode(stream, encoderOfObject);// you need something that can encode T, I give you something that can encode all the way to java.lang.Object
memo
  • 1,739
  • 7
  • 18
0

A few real life examples come to mind for this. The first one I like to bring up, is the idea of a real-world object being used for 'improvised' functionality. Imagine that you have a socket wrench:

public class SocketWrench <T extends Wrench>

The obvious purpose of a socket wrench it so be used as a Wrench. However, if you consider that a wrench could be used in a pinch to pound in a nail, you could have an inheritance hierarchy that looks like this:

public class SocketWrench <T extends Wrench>
public class Wrench extends Hammer

In this scenario, you would be able to call socketWrench.pound(Nail nail = new FinishingNail()), even though that would be considered an atypical use for a SocketWrench.

While all along, the SocketWrench would have access to be able to call methods like applyTorque(100).withRotation("clockwise").withSocketSize(14) if it's being used as a SocketWrench instead of just a Wrench, instead of a Hammer.

John Stark
  • 1,173
  • 1
  • 7
  • 20
  • I don't find this very compelling: why would SocketWrench be a generic? – Max Sep 05 '18 at 15:11
  • You can have all sorts socket wrench types: 1/4” drive, 1/2” drive, adjustable handle angle socket wrenches, torque wrenches, differing ratchet teeth counts, etc. But if you just needed a wrench that ratchets, you could use a generic SocketWrench instead of a specific 3/4” drive 32-tooth angled handle socket wrench. – John Stark Sep 05 '18 at 15:23