سؤال

In the contract of Comparable, there's nothing forcing an object to be comparable to itself. It's just

strongly recommended, but not strictly required that (x.compareTo(y)==0) == (x.equals(y))

which implies that it's recommended for x.compareTo(x) not to throw. But it's possible to write a

class X implements Comparable<Y> {
    ...
}

where X and Y are two unrelated classes. I can't see what it could be good for, but in the Java 8 version of HashMap there's even a corresponding check.

  • Is it allowed to implement X implements Comparable<Y> with two unrelated classes?
  • Does it make any sense?

I guess the answers are yes and no, but it's just a guess

هل كانت مفيدة؟

المحلول 2

I see I missed one part of the contract and also failed to see the reason why HashMap.comparableClassFor exists.


The contract says

(x.compareTo(y)>0 && y.compareTo(z)>0) implies x.compareTo(z)>0

so whenever there's an X greater than a Y and a Y greater than an X, then the two instances of X must be comparable to each other. This doesn't leave much freedom:

  • Either one of the types is empty. This makes no sense at all.
  • Or all instances of X are smaller or equal to all instances of Y (or the other way round). This is slightly less nonsensical.

So, I'm concluding that it's possible, but makes no sense. The simplest example is

class X implements Comparable<Void> {
    public int compareTo(Void v) {
        return 43; // or throw or whatever, it doesn't matter
    }
}

I guess that the reason for HashMap.comparableClassFor is to support different implementations of a common superclass like

abstract class AByteArray implements Comparable<AByteArray> {}
class SparseByteArray extends AByteArray {...}
class DenseByteArray extends AByteArray {...}

This seems to make sense and can be even consistent with equals.

نصائح أخرى

Comparable promotes a contract where comparisons should be consistent with equals, i.e. (a.compareTo(b) == 0) == a.equals(b). But it does not force you to do so and any weird contract can be enforced.

So you could create a:

class DumbInteger implements Comparable<DumbInteger> {
    private final int i;
    public DumbInteger(int i) { this.i = i; }
    public int compareTo(DumbInteger di) { return 0; }
    public boolean equals(Object other) { /* checks */ return other.i == this.i; }
}

And you could also create a:

class DumberInteger implements Comparable<String> {
    private final int i;
    public DumberInteger(int i) { this.i = i; }
    public int compareTo(String s) { return 0; }
    public boolean equals(Object other) { /* checks */ return other.i == this.i; }

    public static void main(String[] args) {
        System.out.println(new DumberInteger(0).compareTo("abc"));
    }
}

but there is probably no point in doing that. In any case this is not specific to Java 8 as the Comparable interface has been there since Java 2 and "generified" in Java 5.

But it is probably not a flaw in the Comparable interface per se, because I don't think there is a way in Java to create a generic interface I<T> that can only be implemented by classes that are subtypes of T.

Does it make any sense?

One issue of having two classes Comparable with each other, is because it tightly couples these classes together. This makes it difficult to re-use the class in another scenario.

Just tried it, it is possible to compare two objects with different classes.

Here is the full code.
https://gist.github.com/cevaris/11099129

X x = new X();
x.xTest = 10;

Y y = new Y();
y.yTest = 100;

System.out.println("x.compareTo(y) == -1: " + (x.compareTo(y) == -1)); //True
System.out.println("y.compareTo(x) == 1: " + (y.compareTo(x) == 1));   //True

Here is the Y implementation.

class Y implements Comparable<X> {
    
    int yTest;

    @Override
    public int compareTo(X o) {
        if(this.yTest < o.xTest) return -1;
        if(this.yTest > o.xTest) return 1;
        return 0;
    }
    
}

Here is the X implementation.

class X implements Comparable<Y> {
    
    int xTest;
    
    @Override
    public int compareTo(Y o) {
        if(this.xTest < o.yTest) return -1;
        if(this.xTest > o.yTest) return 1;
        return 0;
    }

}

Well, it can technically be done (as per @cevaris' answer) and can be understood when you have several ways of representing the same object, e.g. an object and its String representation. But it would only make sense if you could implement the same interface twice like:

public class CompInt implements Comparable<CompInt>, Comparable<String> {

but that is forbidden in Java because of type erasure.

مرخصة بموجب: CC-BY-SA مع الإسناد
لا تنتمي إلى StackOverflow
scroll top