It works because both Integer and String implement Comparable. So it can compile that line, interpreting T = Comparable. The thing is, Comparable is a generic class as well, and for complete use of generics (which would prevent you comparing different types like that) you need to do something like this:
Then t.findLarger(123, "456") won't compile because Integer implements Comparable<Integer>, and String implements Comparable<String>, so it can't find a single value of T to match them both.
Joined: Dec 14, 2009
Thanks for replying. But I still don't get in the former case where it was <T extends Comparable>, You interpreted it as T= Comparable and in your case where <T extends Comparable<? super T>> we are unable to compile.
If possible can you please explain the flow of it.
Because your original code is not type-safe. I haven't run it, but I'm pretty sure it would throw a ClassCastException when you tried finding the larger of an Integer and a String.
You are using a raw type. Comparable is a generic interface, and you're using its raw form. The compiler will actually warn you about this. Instead, you need to parameterize Comparable, so the compiler will tell you you're doing something wrong when you're trying to compare an Integer to a String.
You could do something like public <T extends Comparable<T>> T findLarger(T x, T y) to resolve this issue, but this has a big limitation. You can only compare types that directly implement Comparable, but not their subclasses.
For instance, let's say you have a class Fruit implements Comparable<Fruit>. If Fruit has a subclass Apple, then you can not use your method to compare two apples, because an Apple is not a Comparable<Apple>, it's a Comparable<Fruit>.
So instead of using <T extends Comparable<T>>, we use <T extends Comparable<? super T>>, so we can also use it to get the larger of two apples.
Using this declaration will make sure that the method works as intended, but at the same time compilation will fail if you try to compare incompatible types. Perfect!
The mind is a strange and wonderful thing. I'm not sure that it will ever be able to figure itself out, everything else, maybe. From the atom to the universe, everything, except itself.
I'll try. In the first case:
You can call this with any arguments where you can pick a T that fits them all.
Now, if you call t.findLarger(123, "456"), what can T be? The first argument is an int, which will be boxed to an Integer. So T can be Integer, or any supertype of Integer. Which means Number, Object, Serializable and Comparable (implemented interfaces are possible supertypes as well). But there's a constraint: T extends Comparable. That rules out Number, Object and Serializable. T must be either Integer or Comparable.
Now consider the second argument. It's a String. That would match T being String, Object, Serializable, Comparable or CharSequence. Again, the constraint rules out some of them, so T can be String or Comparable.
The line will compile if we can pick a T that matches both arguments. We can: T = Comparable.
As you've already realised, that lets you do things that don't make sense, like comparing an integer and a string. But, Comparable is actually a generic interface, and if we give it a generic type we can make the compile-time checking stronger. So we can try:
Now if we try and call t.findLarger(123, "456"), what can T be?
Looking at the first argument, and considering the constraint, T can be Integer, or Comparable<Integer> (which is implemented by Integer). Looking at the second argument, T can be String or Comparable<String>. Which means that there's no choice of T that matches both arguments. So the compiler will disallow it.
The version I gave before:
is actually a more flexible version that does the same thing. Since Integer implements Comparable<Integer>, and String implements Comparable<String> you don't need this more flexible version. But an example where you would is the class java.sql.Date (used with databases). This extends java.util.Date, and implements Comparable<java.util.Date>. So a java.sql.Date argument could never be used with a constraint like T extends Comparable<T>, but it could be used with T extends Comparable<? super T>. This last bit is a bit complicated, though, so don't worry too much if it doesn't make sense at the moment. Just try and understand the difference between the first two examples.