I'm trying to learn concepts of generic programming in Java, but I stuck on one question.Consider these implementations of function max:
1)
public static <T extends Comparable<? super T>> T max(Collection<? extends T> collection){
//code
}
2)
public static <T extends Comparable<T>> T max(Collection<T> collection){
//code
}
I'm curious to know, what is the difference between them?
Of course, I know, that declaration Collection<? extends T>
allows to pass subtypes of T as Collection, but what is the use of it in this case, in static method?
The result has to be the same without using bounds, hasn't it?
And what about this <T extends Comparable<? super T>>
?
If you pass subtype of T, it will be okay anyway, because T implements Comparable and implementation of Comparable will maintain in subtype, so you can use 2) option.
I'm just wondering whether there are some cases, when it's impossible to use 2) option, but possible to use 1)
Lets break this down into parts:
<T extends Comparable<? super T>>
This defines the generic bounds of T
- it can represent any type that is Comparable
- either because T
is Comparable
or because some parent class is. Since that parent class (call it P
) likely implements Comparable<P>
rather than Comparable<T>
it's necessary to use super
here. It sounds like you know this already.
T
This is the return type of the method, and can often be specified by the calling context (for instance when you assign the result to a variable). We'll come back to this.
Collection<? extends T>
This allows you to pass in a Collection
of T
s or any subtype of T
(call it S
). If you just specified Collection<T>
you'd only be able to pass Collection<T>
objects and not Collection<S>
objects.
Putting them all together this lets callers do something like:
// notice that max() returns a C, but is assignable to a B
B b = max(new ArrayList<C>());
Where A
, B
, and C
are:
A implements Comparable<A>
B extends A
C extends B
It turns out, since your method returns a T
directly, that the Collection<? extends T>
doesn't really matter because you can always assign a child type (C
) to a parent type (B
). But if we tweak the method signature slightly to return a type using T
as a generic this doesn't work:
public static <T extends Comparable<? super T>> Collection<T>
collect(Collection<T> collection)
Notice we're now returning a Collection<T>
- now the ? extends
becomes important - this doesn't compile:
Collection<B> b = collect(new ArrayList<C>());
Whereas if you change the parameter to Collection<? extends T>
you loosen the bounds; the return type doesn't have to be the same as the parameter type. You can pass in a Collection<C>
but because the caller makes T
mean B
(since that's the type being expected as the return type) C
now extends T
, rather than is T
.
So in short, for your method I don't believe you need the ? extends T
, but in some cases you do. More generally if you intend to support subtypes in your generics it's a good idea to do so explicitly, even if it's not strictly necessary.