generic methods and wildcards - java

What are the differences between the following three signatures?
static <T> void foo(List<T>, Comparator<? super T>);
static <T> void bar(List<? extends T>, Comparator<T> );
static <T> void baz(List<? extends T>, Comparator<? super T>);
I know what extends and super mean in Generics. My question is whether there is a difference between foo, bar and baz. Should I make one of the parameters invariant and the other one variant in the appropriate direction, or should I make both of them variant? Does it make a difference?

PECS - Producer extends, consumer super.
To explain this "rule":
extends means the genericized object produces elements of the type. When it's a collection, it means you can only take elements from the collection, but not put them in. The comparator
super means the object consumes objects of the selected type. So you can add to the collection, but not read from it.
the lack of extends and super means you can do both for the exact type specified.
Regarding the Comparator, I don't think it makes any difference. Normally, it would be <? super T> because you the comparator consumes objects, but in all three cases you can safely invoke Collections.sort(list, comparator); (the signature of which is <? super T>)

The only difference is whether T represents the type parameter of the List, the Comparator, or something in between.
As far as the caller is concerned, the three method signatures are equivalent, i.e. whenever one of them can be used, the others can be used as well.
For the method implementation foo is probably most convenient, as it allows modifying the list without needing an additional capture conversion, which would require delegating to a helper method.

I believe that ? extends T means that the List may be generic of any type that is derived from T, whereas List<T> can only be a List of T and not any of its derived classes.

Related

What is the right signature of constant comparator or method for subclasses?

I have an abstract class looks like this.
abstract class Some {
int rank;
}
Now I want to define a static variable and/or method of comparing the rank.
static <T extends Some> Compartor<T> comparingRank() {
return Comparator.compartingInt(Some::getRank);
}
static final Compartor<Some> COMPARING_RANK
= Compartor.comparingInt(Some::getRank);
My questions are...
Should I use <? super T> for comparingRank() method?
Should I use <? super T> for COMPARING_RANK field?
We generally don't encourage to use wildcards in the return type of the method, since that renders the method unusable. So, the first option is ruled out due to that.
The second variant of using a lower bound wildcard ? super T is generally a good thing which makes your API much more flexible. But, in this particular scenario, the comparingInt declares that wildcard, leaving yours superfluous.
static <T> Comparator<T> comparingInt(ToIntFunction<? super T> keyExtractor)
Thus, in my opinion, you can keep it as it is without any modification. However, you may still use the wildcard where circumstances warrant.

Why Comparable and Comparator are consumers in PECS wildcard types in Java

In Effective Java, in the item "Use bounded wildcards to increase API flexibility", when talking about the usage of PECS (producer-extends, consumer-super), the author mentioned that:
Comparables are always consumers, so you should generally use Comparable<? super T> in preference to Comparable. The same is true of comparators; therefore, you should generally use Comparator<? super T> in preference to Comparator.
It is not clear to me why Comparables and Comparators are considered consumers.
In one of the topic discussing PECS, What is PECS (Producer Extends Consumer Super)?, the consumer is usually referring to a Collection as a parameter for some generic method.
While here Comparable is just an interface.
Can anyone share some insights? Thanks!
A nice analogy can be drawn to the interfaces Consumer<T> and Supplier<T> (with Supplier being analogous to Producer). A Consumer<T> is a function that takes in a T, while a Supplier<T> is a function that returns a T. Notice that we are talking about method signatures and return type, we say nothing about the semantics of the method. This is a core property of PECS: it is independent of the semantics and can be determined solely on the signature and return type of the methods used.
Looking at Comparable<T> and Comparator<T>, we find that both have methods (int compareTo(T) and int compare(T, T)) that take in, i.e. consume, T's.
For the collections, we have to look on how we use the collection, i.e. if we are using producer- or consumer-methods:
If we retrieve data from the collection (T get(int), iterator, ...), the list produces values for us and we use ? extends T.
If we use the collection to store data, (i.e. we call add(T), addAll(Collection<T>), contains(T), ...), we call consuming methods, thus the method is a consumer of our data and we use ? super T.
If we use a collection to both store and retrieve values, the collection acts as a consumer and producer at the same time, thus we have to use the precise T, neither using ... extends ... nor ... super ....
„…It is not clear to me why Comparables and Comparators are considered consumers.…“
Anytime a method member of a generic class C<T>, takes in (i.e. „consumes“) an argument that is of type T, then that method is a consumer of Ts.
So Comparator<T>.compareTo(T o) is said to be a „consumer“ of the o object of the type represented by the type variable T.
When we say "Comparable is a consumer", we actually mean "the method of the interface is a consumer". This is an answer to your doubt that "Comparable is just an interface."
See its signature: https://docs.oracle.com/en/java/javase/14/docs/api/java.base/java/lang/Comparable.html#compareTo(T)
int compareTo​(T o)
It consumes the generic T, it does not produce any generic object.

Java Type Wildcarding

(For the purposes of this post, lets set aside java.util.Observable)
I was experimenting around with generics, and then wildcard types. The aim was to create a type-generic observable cache with deltas provided to the observers. Where this starts to go off the rails is I wanted to allow more generic observers to be used than the one specified in the Observable, e.g. Observer<Object> or some other common superclass.
I've since concluded that this is overly complex for my use case, but the problem itself continues to bother me since I clearly don't understand how to use type wildcarding properly.
So if we start with a simple observer interface:
public interface Observer<T> {
public void notifyChange(ChangeHolder<T> change);
}
And the associated ChangeHolder, in a full implementation this would be more complex, providing lists of added / updated / deleted objects, but this is sufficient to demonstrate the issue
public interface ChangeHolder<T> {
T getChange();
}
So with the Observer defined, I tried to implement the Observable abstract class:
public abstract class Observable<T> {
private Set<Observer<? super T>> observers = new HashSet<>();
public void addObserver(Observer<? super T> obs){
observers.add(obs);
}
public void change(ChangeHolder<T> changes){
for(Observer<? super T> obs : observers){
obs.notifyChange(changes);
}
}
}
And with that I could define some object caches, by declaring something like class TreeCache extends ObservableCache<Tree>, (From this point on I'll use Tree as an example class to be used as a T, assume it to be a simple POJO extending only from Object) and pass ChangeHolder<Tree> objects to TreeCache.change() when necessary. Unfortunately the compiler disagrees:
The method notifyChange(ChangeHolder<capture#2-of ? super T>) in the type Observer<capture#2-of ? super T> is not applicable for the arguments (ChangeHolder<T>)
Which is where my understanding ends.
Without the ChangeHolder class (if my notifyChange method just took a plain T instead) it works just fine since it's perfectly legal to pass a Tree to Observer.notifyChange(Object).
I inferred that I should be able to do the same with the ChangeHolder - ChangeHolder<T> should satisfy notifyChange(ChangeHolder<? super T>) in the same way that T satisfies notifyChange(? super T) but clearly I am misunderstanding something?
There is no wildcard in the signature notifyChange(ChangeHolder<T> change). Therefore the generic type of the passed argument must exactly match the generic type of the Observer instance.
Observer<? super T> means an Observer of some unknown type that is a supertype of T. Since the generic type of obs may not exactly match the generic type of changes, the notifyChange method is not applicable.
There are two possible fixes:
Change the signature to notifyChange(ChangeHolder<? extends T> change) so that the method works for subtypes.
Get rid of the wildcards everywhere, so that you have just <T> instead.
I prefer solution 1, as it is a good idea for signatures to be as general as possible.

Generics Collections PECS

I have a question about this method from java.util.Collections:
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 understand how <? super T> works, however, I don't understand why the first parameter is List<? super T> instead of List<T>. I think it's useless in this situation.
Using List<T> should work as well, shouldn't it?
Could you give me some examples to understand it if possible, please?
Thanks.
No, it makes sense. For example, consider this situation:
T is InputStream
dest is a List<Object>
src is a List<FileInputStream>
That works absolutely fine. Of course, you could make T either Object or FileInputStream in this situation - but imagine you were calling this from a method with a signature of:
public void doSomething(List<? super InputStream> streams) {
// I want to use copy in here for some reason
}
You don't know it's a List<InputStream> - only that it's a List<? super InputStream>. If the dest parameter in copy were just List<T>, we'd be stuck... but with the way it is written, we're fine.
It also makes sense in terms of what we require from the destination list - we just need to be able to set values of T within it. Likewise all we require of the source list is that we can get values of T from it. <? super T> and <? extends T> express those requirements well.
If you break it down in why the List is being used it will be a bit more clear.
When a method intends to populate a list, you could restrict it to use a specific type T, however often you might want to be less restrictive.
For example, lets say you have a method populateWithStudents(List<Student> list)
And you have Student extend Person
This means that you can't use that method with a List<Person> to fill it with Student objects, even though Student extends Person.
So if on the other hand we would like to allow that we change it to populateWithStudents(List<? super Student> list). This way we're saying that as long as we can fit a Student in the list, no matter if it is a list of Student objects, or a list of any of its superclasses, it is allowed.
This is not just when populating obviously, but this example helps understand the concept.

Why is returning List<? extends T> read-only and returning List<? super T> write-only? [duplicate]

This question already has answers here:
Closed 10 years ago.
Possible Duplicate:
What is a difference between <? super E> and <? extends E>?
Some Java programmers on a team I'm on are writing functions that return objects of type List<? extends T> to make read-only lists and return objects of type List<? super T> to make write-only lists.
In Java, what makes List<? extends T> read-only and List<? super T> write-only?
please read up on "producer extends, consumer super" (PECS) - I may need to do the same :)
Read only:
In case you would like to ensure that a method takes as a parameter a collection of items ( using generics) - when you use List<? extends T> - the list can contain any subtype of T but cannot add to the collection since it does not know at runtime the specific type of T that the List contains.
Write only:
For List<? super T>, the list can contain T regardless of the actual parameterized type (using super will allow that to happen).
Hope it helps.
You can get a T from a List<? extends T>, but the only thing you can put into it is a null literal. You can put a T into a List<? super T>, but the only thing you can get from it is an Object (which is then unsafe to cast down to T).
So, the restrictions make these pretty good, though imperfect, reminders of the intention. With a List<? extends T>, you can't put most things into it -- so it's kinda read-only. And with a List<? super T>, you can't get things out of it very usefully -- so it's kinda write-only.
Note that neither one of these is actually read- or write-only. I noted some of the ways you can get things into or out of them above, and with the "read-only" construct, you can still call remove functions, either on the object itself or on its iterator.
The definition List<? extends T> means "a List<X> implementation, where X must be T or a subclass/implementation thereof. In other words, your only guarantee is that objects already stored in the list will fit in a variable of type T. However, since the list may have been declared for a class extending or implementing T, you have no guarantee that objects of type T itself will fit into the list.
Simple example: List<String> is a List<? extends T> if T is Object. You can safely draw Object-type objects from it, but you can obviously not put other Object-type objects in it, unless they are definitely strings.

Categories

Resources