Василь Палій Василь Палій - 4 months ago 18
Java Question

Generics in Java

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)

Answer

To fully understand it, you're missing two versions. Here are all four:

public static <T extends Comparable<? super T>> T max1(Collection<? extends T> collection){
    throw new UnsupportedOperationException("Not yet implemented");
}
public static <T extends Comparable<T>> T max2(Collection<T> collection){
    throw new UnsupportedOperationException("Not yet implemented");
}
public static <T extends Comparable<T>> T max3(Collection<? extends T> collection){
    throw new UnsupportedOperationException("Not yet implemented");
}
public static <T extends Comparable<? super T>> T max4(Collection<T> collection){
    throw new UnsupportedOperationException("Not yet implemented");
}

To see how they work, I'll steal the example by @MattTimmermans:

class A implements Comparable<A> {
    @Override public int compareTo(A that) {
        throw new UnsupportedOperationException("Not yet implemented");
    }
}
class B extends A {
}

Now let's see which of the four max methods work:

List<B> list = new ArrayList<>();

B maxItem1  = max1(list);         // T = B (but could be A if result is A, see next line)
A maxItem1a = Test.<A>max1(list); // T = A
B maxItem1b = Test.<B>max1(list); // T = B

B maxItem2  = max2(list);         // compile error

A maxItem3  = max3(list);         // T = A (forced by Comparable<T>)

B maxItem4  = max4(list);         // T = B (forced by Collection<T>)

As you can see, for max3() and max4(), T is being forced to a specific type by the lack of a wildcard.

max2() doesn't work at all, because there is no T that can satisfy both Comparable<T> and Collection<T>.

As for max1(), T can be resolved to either A or B, but if it is resolved to A, then assignment must be to a variable of type A. If resolved to B, assignment can be to either A or B.
If you don't manually specify a type for T, the compiler will choose B.