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.
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 .
Generics add that type of safety feature. We will discuss that type of safety feature in later examples. Generics in Java are similar to templates in C++. For example, classes like HashSet, ArrayList, HashMap, etc., use generics very well.
super is a lower bound, and extends is an upper bound.
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) { .... }
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.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With