- https://www.youtube.com/watch?v=V1vQf4qyMXg&t=1344s&ab_channel=UserGroupsatGoogle
- https://stackoverflow.com/questions/4343202/difference-between-super-t-and-extends-t-in-java
1 - PECS (Producer Extends, Consumer Super)
USED IN METHOD PARAMETER - AVOID IN RETURN TYPE
- “Producer Extends” -
public void method(List<? extends T> list)- cannot add any object to this list
- can guarantee object of type T to be read from this list
- “Consumer Super” -
public void method(List<? super T> list)- can add objects of type T and subtypes of T to this list
- cannot guarantee what type of object you may read from this list
- If you need to both read from and write to a list, you need to declare it exactly with no wildcards (e.g.
List<T>)
|
Parameter Produces | |||
|---|---|---|---|
|
YES |
NO | ||
|
Parameter Consumes T Instances? |
YES |
|
|
|
NO |
|
| |
2 - Examples
Example 1
Note how the source list src (the producing list) uses extends, and the destination list dest (the consuming list) uses super:
public static <T> void copy(List<? extends T> src, List<? super T> dest) {
for (int i = 0; i < src.size(); i++) {
T t = src.get(i);
// T t = dest.get(i); <-- compiler error
// T t = (T) dest.get(i); <-- may throw ClassCastException
dest.set(i, t);
// src.set(i, t); <-- compiler error
}
}
Example 2
public class SortedList<T extends Comparable<? super T>> extends LinkedList<T>
T extends Comparable<? super T> is saying that EITHER:
- type
Thas to implement Comparable - type
T’s superclass has to implement Comparable
So consider java.util.Date. It implements Comparable<Date>. But what about java.sql.Date? It implements Comparable<java.util.Date> as well.
Without the super signature, SortedList would not be able accept the type of java.sql.Date, because it doesn’t implement a Comparable of itself, but rather of a super class of itself
Example 3
<R> Stream<R> map(Function<? super T, ? extends R> mapper);having map take in the parameter Function<? super T, ? extends R> allows the following:
Function<Number, String> func = String::valueOf;
List<Integer> list = List.of(1, 2);
Stream<String> stream = list.stream().map(func);
List<Long> list = List.of(1L, 2L);
Stream<String> stream = list.stream().map(func);3 - Explanation
Imagine having this hierarchy
|
|
|
3.1 - Extends
By writing
List<? extends C2> list =
you are saying that list will be able to reference an object of type (for example) ArrayList whose generic type is one of the 7 subtypes of C2 (C2 included):
new ArrayList<C2>() - can list.add: C2 D1 D2 E1 E2 E3 E4new ArrayList<D1>() - can list.add: D1 E1 E2new ArrayList<D2>() - can list.add: D2 E3 E4new ArrayList<E1>() - can list.add: E1new ArrayList<E2>() - can list.add: E2new ArrayList<E3>() - can list.add: E3new ArrayList<E4>() - can list.add: E4
We have a set of “storable” types for each possible case: 7 sets here graphically represented
/java-platform/java/java---projects--and--code-examples/java---native-libraries/java---(?-extends-t)-vs-(?-super-t)/java-extends-vs-super-2.png)
As you can see, there is not a safe type that is common to every case:
- you cannot
list.add(new C2())because it could belist = new ArrayList<D1>(); - you cannot
list.add(new D1())because it could belist = new ArrayList<D2>();
and so on.
3.2 - Super
By writing
List<? super C2> list =
you are saying that list will be able to reference an object of type (for example) ArrayList whose generic type is one of the 7 supertypes of C2 (C2 included):
new ArrayList<A1>() - can list.add: A1 B1 B2 C1 C2 D1 D2 E1 E2 E3 E4new ArrayList<A2>() - can list.add: A2 B2 C1 C2 D1 D2 E1 E2 E3 E4new ArrayList<A3>() - can list.add: A3 B3 C2 C3 D1 D2 E1 E2 E3 E4new ArrayList<A4>() - can list.add: A4 B3 B4 C2 C3 D1 D2 E1 E2 E3 E4new ArrayList<B2>() - can list.add: B2 C1 C2 D1 D2 E1 E2 E3 E4new ArrayList<B3>() - can list.add: B3 C2 C3 D1 D2 E1 E2 E3 E4new ArrayList<C2>() - can list.add: C2 D1 D2 E1 E2 E3 E4
We have a set of “storable” types for each possible case: 7 sets here graphically represented
/java-platform/java/java---projects--and--code-examples/java---native-libraries/java---(?-extends-t)-vs-(?-super-t)/java-extends-vs-super-3.png)
As you can see, here we have seven safe types that are common to every case: C2, D1, D2, E1, E2, E3, E4.
- you can
list.add(new C2(){})because, regardless of the kind of List we’re referencing,C2is allowed - you can
list.add(new D1(){})because, regardless of the kind of List we’re referencing,D1is allowed
and so on. You probably noticed that these types correspond to the hierarchy starting from type C2.
/java-platform/java/java---projects--and--code-examples/java---native-libraries/java---(?-extends-t)-vs-(?-super-t)/java-extends-vs-super-1.png)