Note: I merely modified the equivalent rule for equals to make this rule.
It also seems like I'm sort of just copying from the Java standard,
but I can't think of any reason why if we have an equals rule
we should not have a compareTo rule, since it is so often used with equals.
This rule could be extended to deal with Comparator as well; since they go together.
The general usage contract for compareTo() has been put forth verbatim from the Java specification:
The implementor must ensure sgn(x.compareTo ) == -sgn(y.compareTo
) for all x and y. (This implies that x.compareTo
must throw an exception iff y.compareTo
throws an exception.)
The implementor must also ensure that the relation is transitive: (x.compareTo >0 && y.compareTo(z)>0) implies x.compareTo(z)>0.
Finally, the implementor must ensure that x.compareTo ==0 implies that sgn(x.compareTo(z)) == sgn(y.compareTo(z)), for all z.
Do not violate any of five conditions while overriding the compareTo method.
Noncompliant Code Example
This noncompliant code example violates the third condition in the contract.
Consider a Card that considers itself equal to any card of the same suit; otherwise it orders based on rank.
public final class Card implements Comparable{
private String suit;
private int rank;
public Card(String s, int r) {
if (s == null)
throw new NullPointerException();
suit = s;
rank = r;
}
public boolean equals(Object o) {
if (o instanceof Card){
Card c=(Card)o;
return suit.equals(c.suit) || (rank == c.rank);
}
return false;
}
//this method violates its contract
public int compareTo(Object o){
if (o instanceof Card){
Card c=(Card)o;
if(suit.equals(c.suit)) return 0;
return c.rank - rank;
}
throw new ClassCastException();
}
public static void main(String[] args) {
Card a = new Card("Clubs", 2);
Card b = new Card("Clubs", 10);
Card c = new Card("Hearts", 7);
System.out.println(a.compareTo(b)); //returns 0
System.out.println(a.compareTo(c)); //returns a negative number
System.out.println(b.compareTo(c)); //returns a positive number
}
}
Compliant Solution
Make sure you fulfill the contract, and make sure your corresponding equals method matches with compareTo.
public final class Card implements Comparable{
private String suit;
private int rank;
public Card(String s, int r) {
if (s == null)
throw new NullPointerException();
suit = s;
rank = r;
}
public boolean equals(Object o) {
if (o instanceof Card){
Card c=(Card)o;
return suit.equals(c.suit) && (rank == c.rank);
}
return false;
}
//this method fulfills its contract
public int compareTo(Object o){
if (o instanceof Card){
Card c=(Card)o;
if(suit.equals(c.suit)) return c.rank - rank;
return suit.compareTo(c.suit);
}
throw new ClassCastException();
}
public static void main(String[] args) {
Card a = new Card("Clubs", 2);
Card b = new Card("Clubs", 2);
Card c = new Card("Hearts", 7);
System.out.println(a.compareTo(b)); //returns 0
System.out.println(a.compareTo(c)); //returns a negative number
System.out.println(b.compareTo(c)); //returns a negative number
}
}
Risk Assessment
Violating the general contract when overriding the compareTo() method can lead to unexpected results.
Rule |
Severity |
Likelihood |
Remediation Cost |
Priority |
Level |
|---|---|---|---|---|---|
MET30-J |
low |
unlikely |
medium |
P2 |
L3 |
Automated Detection
TODO
References
Java API