For callers, the second version is roughly equivalent
public static <T, X extends Comparable<? super T>> int methodX(List<X> data)
Suppose the caller calls it using arg, whose specific type is List<Foo> . Type inference concludes that X=Foo . Then we get a new equation for T of X related
=> Foo <: Comparable<? super T>
( A <: B means that A is a subtype of B)
If Foo is comparable at all, it is almost certainly implements Comparable<Foo> [2]
=> Comparable<Foo> <: Comparable<? super T> => T <: Foo
Without additional information, the output selects T=Foo .
Therefore, from the calling POV, the two versions do not differ from each other.
Inside the method body, the second version does not have access to a parameter of type X , which is synthetic, introduced at the compilation stage. This means that you can only read data . Such things as
X x = data.get(0); data.set(1, x);
impossible in version number 2; There is no such problem in version No. 1 with T
However, we can forward # 2 to # 1
<T1> method1(List<T1> data){ data.set(...); } <T2> method2(List<?...> data) { method1(data); } (they must have difference method names; overloading not allowed since java7)
This is because for the compiler, the data type is really List<X> (he knows the secret of X ), so there is no problem calling method1(data) after the conclusion that T1=X
[1] JLS3, 5.1.10 Capture Conversion
[2] According to javadoc Comparable , this interface imposes full ordering on the objects of each class that implements it. This means that if Foo implements Comparable<W> , W must be Foo or a super-type Foo. It is absolutely unbelievable that the implementation of the subclass determines the general order among the objects of the superclass. Therefore, W most definitely be Foo . Otherwise, funny things will happen. A notorious example: "Timestamp", its javadoc (now) explains why it cannot be compared with its supertype Date