Un certain nombre de classes sont munies d'un ordre naturel : Byte, Short, Integer, Long, Float, Double, BigInteger, BigDecimal (ordre numérique), Character (ordre alphabétique), File (ordre lexicographique sur le chemin d'accès), String (ordre lexicographique), Date (ordre chronologique).
Ce sont les classes réalisant l'interface Comparable, membre du paquet java.lang, qui déclare une méthode de comparaison des objets :
package java.lang; public interface Comparable { int compareTo(Object o); }
La documentation indique que la méthode compareTo, appliquée à un objet 70#70, avec pour argument un objet 71#71, compare les objets 70#70 et 71#71 et retourne un entier 138#138, ou 237#237 ou un entier 210#210, selon que 70#70 est plus petit que 71#71, lui est égal ou lui est supérieur ; elle déclenche l'exception ClassCastException si le type de 71#71 ne permet pas la comparaison avec 70#70. La documentation ne dit pas comment cette comparaison s'effectue, ce qui n'aurait évidemment aucun sens pour des objets quelconques : une telle méthode de comparaison n'est d'ailleurs définie que dans des classes qui réalisent l'interface Comparable. On dit alors qu'une telle classe est munie d'un ordre naturel. Ainsi, les classes Character, Double, String, Integer réalisent cette interface, mais la classe Object ne la réalise pas.
Par exemple, la classe Integer pourrait réaliser l'interface Comparable grâce à la définition suivante de compareTo :
package java.lang; public class Integer { // ... public int compareTo(Object o) { int n = ((Integer)o).intValue(); int m = this.intValue(); return (m<n ? -1 : (m==n ? 0 : 1)); } }
Toute interface est un sous-type d'Object. Une classe qui réalise une interface est un sous-type de cette interface. On peut donc affecter à une variable de type l'interface une expression d'une classe la réalisant, par exemple :
Comparable c = new Integer(3);
Si nous voulons définir une fonction min qui calcule le minimum de deux objets, au lieu de lui passer un comparateur en 3ème argument, on peut supposer que son premier argument est comparable à son second argument : il suffit de déclarer le premier argument de type Comparable :
static Object min(Comparable x, Object y) { return x.compareTo(y) <=0 ? x : y; }
On pourra alors invoquer cette fonction, par exemple, sur des instances d'Integer5.1 :
Object m = min(new Integer(3), new Integer(2));
ou, si l'on veut obtenir un Integer, à l'aide d'un transtypage :
Integer m = (Integer) min(new Integer(3), new Integer(2));
Il est facile de lui donner un ordre naturel en lui ajoutant une méthode compareTo, par exemple pour l'ordre lexicographique sur les noms :
class Point implements Comparable { // ... public int compareTo(Object o) { Point p = (Point) o; int dx = x - p.x; return dx!=0 ? dx : y - p.y; } // ... }
On notera que les méthodes equals et compareTo se comportent différemment si l'objet n'a pas le type requis, ici Personne : le test o instanceof Personne permet à equals de retourner false, tandis que compareTo, ne procédant pas à ce test, peut déclencher l'exception ClassCastException due au transtypage (Personne) o.
Les contraintes (sur equals, compareTo, hashCode, etc.) doivent être respectées, afin d'assurer à l'utilisateur que les méthodes qui les utilisent (par exemple, Collections.sort, etc.) font bien ce qu'elles sont censées faire.