What is a real life example of generic <? super T>?

JavaGenericsInheritanceSuper

Java Problem Overview


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.

Java Solutions


Solution 1 - Java

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) {
    ....
}

Solution 2 - Java

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.

Solution 3 - Java

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

Solution 4 - Java

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>

Solution 5 - Java

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.

Solution 6 - Java

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).

Solution 7 - Java

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

Solution 8 - Java

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

Solution 9 - Java

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.

Attributions

All content for this solution is sourced from the original question on Stackoverflow.

The content on this page is licensed under the Attribution-ShareAlike 4.0 International (CC BY-SA 4.0) license.

Content TypeOriginal AuthorOriginal Content on Stackoverflow
QuestionBanzaiTokyoView Question on Stackoverflow
Solution 1 - JavaEugeneView Answer on Stackoverflow
Solution 2 - JavaBenoitView Answer on Stackoverflow
Solution 3 - JavaOleksandr PyrohovView Answer on Stackoverflow
Solution 4 - JavaxingbinView Answer on Stackoverflow
Solution 5 - JavaItFreakView Answer on Stackoverflow
Solution 6 - JavayshavitView Answer on Stackoverflow
Solution 7 - JavaMichał TurczynView Answer on Stackoverflow
Solution 8 - JavamemoView Answer on Stackoverflow
Solution 9 - JavaJohn StarkView Answer on Stackoverflow