Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Declaration of Comparable interface

Tags:

java

generics

When the Comparable interface was made generic, the declaration became

interface Comparable<T>

Really it should be something like

interface Comparable<T extends Comparable<T>>

It makes no sense for the T to not extend Comparable because implementors must ensure that

a.compareTo(b)

and

b.compareTo(a)

always have opposite signums.

I have always assumed that the reason for getting the declaration "wrong" was something to do with the problems of generifying an existing interface, but I can't really explain it.

Does anyone have any insights?

like image 811
Paul Boddington Avatar asked Oct 22 '17 19:10

Paul Boddington


People also ask

What is a comparable interface?

The Comparable interface is used to compare an object of the same class with an instance of that class, it provides ordering of data for objects of the user-defined class. The class has to implement the java.

What is a comparable interface in Java?

The Java Comparable interface, java. lang. Comparable , represents an object which can be compared to other objects. For instance, numbers can be compared, strings can be compared using alphabetical comparison etc. Several of the built-in classes in Java implements the Java Comparable interface.

Can we implement comparable interface?

We can implement the Comparable interface with the Movie class, and we override the method compareTo() of Comparable interface.

When a class implements to comparable interface what method must also be implemented?

For any class to support sorting, it should implement the Comparable interface and override it's compareTo() method. It returns a negative integer if an object is less than the specified object, returns zero if an object is equal, and returns a positive integer if an object is greater than the specified object.


1 Answers

Really it should be something like

interface Comparable<T extends Comparable<T>>

But that doesn't really give you anything that you don't get with interface Comparable<T>.

You mention the thing about a.compareTo(b) and b.compareTo(a), but note that your declaration (interface Comparable<T extends Comparable<T>>) doesn't actually ensure that if a.compareTo(b) is valid, that b.compareTo(a) compiles. If a is Comparable<T>, a.compareTo(b) requires that b be T, which is also Comparable<T>, so b.compareTo() takes a T. That doesn't prove that b.compareTo(a) works, as a is a Comparable<T>. As a case in point, consider class Foo implements Comparable<Foo>, and class Bar implements Comparable<Foo>. If a is of type Bar and b is of type Foo, then with your declaration of Comparable, a.compareTo(b) compiles but b.compareTo(a) doesn't compile (which is also the same thing that happens with the original declaration of Comparable). To have a bound that guarantees that when a.compareTo(b) works, that b.compareTo(a) also works, you would need to have something like interface Comparable<T extends Comparable<Comparable<T>>>, but that would be a useless interface because nobody has a T that is comparable to Comparable<T>.

More fundamentally, the purpose of adding a bound in Generics is to allow you to compile something that wouldn't compile, or would require a cast, without the bound. Remember, Java checks that code is type-safe at compile-time -- it will only allow you to compile something it knows is possible with the types declared at compile-time (unless you add explicit casts, in which case you take responsibility for the correctness of the conversion). So adding a bound will not increase type-safety -- with or without the bound, the Java compiler will only compile code that is provably type-safe (except for explicit casts or raw type conversions). The difference that a bound makes is that by adding constraints, it allows Java to accept more code as type-safe, because the constraints allow Java to deduce the correctness where it wasn't able to before. So a bound should only be added when it will allow you to compile something you couldn't without the bound, or would need to add explicit casts without the bound. Otherwise, what's the point of adding complexity for no benefit?

Here, you are not going to find any realistic use cases of code that would compile with your declaration of Comparable but would not compile with the original declaration. The only realistic use cases I've seen where a declaration interface Foo<T extends Foo<T>> would allow something to compile that wouldn't with interface Foo<T> is in something like the Builder pattern, where Foo<T> has a method that returns a T, and since we know that T is a subtype of Foo<T>, we can go from a Foo<T> to a Foo<T> and chain these operations, without knowing the specific type of T. But this is not the case with Comparable -- Comparable doesn't have a method that returns T.

If you have a class or method that takes in a comparable type that it needs to sort or order, it is going to have to be a generic method or class that requires the type be comparable to itself (e.g. class SortedList<T extends Comparable<? super T>> or <T extends Comparable<? super T>> void sort(List<T>)). No bound in the declaration of Comparable can ensure that the type is comparable to itself.

If someone decides to write a class where a type is comparable to something completely unrelated (even something that is not Comparable), then that's fine from a type-safety point of view. Yes, you pointed out that it probably violates the documentation of .compareTo(), but that is a code behavior issue, not a type-safety issue. Their class will probably not be very useful because it will not satisfy the bounds of most places which use Comparable, which will likely have bounds that require that the type be comparable to itself. If there is a place that takes Comparable that doesn't have a bound requiring the type to be comparable to itself, then they can use their class there, but that is still type-safe, because the fact that the place didn't require the type to be comparable to itself means that its own code doesn't rely on that fact to compile safely (or they used explicit casts in which case they take responsibility for the correctness). In any case, that someone could potentially write a (mostly useless and maybe violates documentation) class that is comparable to something not comparable doesn't affect your ability to write your own class that is comparable to itself, and there is no type-safety reason to add any bounds.

like image 142
newacct Avatar answered Oct 15 '22 17:10

newacct