In terms of absolute information typing, I do not think there is a difference (but I would be happy to be proved wrong). In fact, it compiles and runs fine if the body of your static method is simply a call to Collections.copy
. A couple key points though, after a lot of thought and experimentation:
- The signature of
Collections.copy
is conceptually much more clear than the one you've suggested. It exemplifies the PECS principle (see also Josh Bloch's Effective Java).
- Looking into source code (OpenJDK 7) of
Collections.copy
, I observe that the type parameter T
is never actually used directly. Your version creates the same type bounding (source must generate a subtype of dest), but without such a clear connotation that there is some well-defined "middle type" where the wildcard bounds meet.
- Perhaps most importantly of all, the only reason your version can be considered equivalent is because it's a static method, and static methods in general don't take full advantage of the power of generics (though that is no reason not to still use the best syntax there). The following example makes this very clear by doing similar operations as instance methods.
Consider a class TypeCopier<T>
which copies parametrized types.
public class TypeCopier<T> {
void copyType(List<? super T> dest, List<? extends T> src) {
// copy
}
public static void main(String[] args) {
TypeCopier<Number> copier = new TypeCopier<>();
copier.copyType(new ArrayList<Object>(), new ArrayList<Integer>());
}
}
Notice a few things about this first example:
- It was easy to specify the type
Number
with a single parameter
- It compiles
- It allows
dest
and src
to be any supertype and subtype of Number
respectively
Now try to make a similar class using the signature you suggested:
public class TypeCopierBad<T, E extends T> {
void copyType(List<T> dest, List<E> src) {
// copy
}
public static void main(String[] args) {
TypeCopierBad<Object, Number> copier = new TypeCopierBad<>();
copier.copyType(new ArrayList<Object>(), new ArrayList<Integer>());
}
}
Notice a few things about this second example:
- It required two type parameters to try to specify the same thing as the one above (and it fails that attempt, as we will see below)
- It does not compile, because
src
is not exactly the Number
type
- It much more tightly constrains what
dest
and src
can be, compared to the previous example