When implementing my own data structures which require a comparable type I've always done so like this:
public class ComparableCollection<E extends Comparable<E>> { ... }
This obviously enforces the comparable constraint at compile time. But I've been a student for the last couple years and have somehow overlooked the fact that the Java implementations for collections that enforce a comparable type do not do so at compile time but rather at runtime by possibly throwing a ClassCastException
while adding an element; e.g.:
public class TreeSet<E> extends AbstractSet<E>
implements NavigableSet<E>, ... { ... }
TreeSet
is backed by a NavigableMap
which, if it's Comparator
is null
, attempts to cast the key like so:
Comparable<? super K> k = (Comparable<? super K>) key;
Now, if the type being inserted isn't comparable a ClassCastException
is thrown.
What are the real benefits of this design over enforcing the constraint at compile time?
The benefit is that you can then use TreeSet with objects that do not implement Comparable
but for which you can provide a Comparator
.
For more about the differences between Comparable an Comparator, see: Java : Comparable vs Comparator