What is a real life example of generic <? super T>?
JavaGenericsInheritanceSuperJava 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
.