30

I want to know why the .compareTo() is in the Comparable interface while a method like .equals is in the Object class. To me, it seems arbitrary why a method like .compareTo() is not in the Object class already.

To use .compareTo(), you implement the Comparable interface and implement the .compareTo() method for your purposes. For the .equals() method, you simply override the method in your class, since all classes inherit from the Object class.

My question is why is a method like .compareTo() in an interface that you implement rather than in a class like Object? Likewise, why is the .equals() method in the class Object and not in some interface to be implemented?

58

Not all objects can be compared, but all objects can be checked for equality. If nothing else, one can see if two objects exist at the same location in memory (reference equality).

What does it mean to compareTo() on two Thread objects? How is one thread "greater than" another? How do you compare two ArrayList<T>s?

The Object contract applies to all Java classes. If even one class cannot be compared to other instances of its own class, then Object cannot require it to be part of the interface.

Joshua Bloch uses the key words "natural ordering" when explaining why a class might want to implement Comparable. Not every class has a natural ordering as I mentioned in my examples above, so not every class should implement Comparable nor should Object have the compareTo method.

...the compareTo method is not declared in Object. ... It is similar in character to Object's equals method, except that it permits order comparisons in addition to simple equality comparisons, and it is generic. By implementing Comparable, a class indicates that its instances have a natural ordering.

Effective Java, Second Edition: Joshua Bloch. Item 12, Page 62. Ellipses remove references to other chapters and code examples.

For cases where you do want to impose an ordering on a non-Comparable class that does not have a natural ordering, you can always supply a Comparator instance to help sort it.

  • 3
    There is even more fun when you start thinking about compareTo with Exception (which isn't an abstract class and thus would have implemented it meaning aNullPointerException.compareTo(anUnsupportedFlavorException) would have... meaning? – user40980 May 7 '15 at 2:43
  • 10
    all objects can be checked for equality In Java yes, but in general no. There are some examples of objects where equality comparison doesn't make sense (not even reference equality) - e.g. singletons. There could be interfaces (abstract classes) like ValueEquality and ReferenceEquality. It might not even be so bad idea ... – qbd May 7 '15 at 9:08
  • 5
    "all objects can be checked for equality. If nothing else, one can see if two objects exist at the same location in memory (reference equality)." -- since we do have == for the latter, this has a hollow ring to it. Disregarding the redundant default, one can find valid reasons not to assume equals on all classes, since not all types may support an equivalence relation. – Raphael May 7 '15 at 12:55
  • 3
    Two examples of types where it's not sensible to define equality: streams (e.g. lazy potentially infinite lists or infinite precision numbers) and functions. The former has the problem that establishing equality may require comparing infinitely. Deciding if two functions are equal is undecidable. Asking if two instances of these types exist in the same memory location is 1) not very useful and 2) allows clients to write code that's sensitive to what should be implementation details. Today I may give you a new instance of the same infinite list each time you ask, tomorrow I may memoize it. – Doval May 7 '15 at 20:26
  • 6
    @Snowman The fact that it's useless combined with the fact that exposes implementation details is reason enough not to allow it. Pretty much every "value-based" class in Java 8 has some boilerplate saying "We're not responsible for what happens if you use ==" because how those classes are instantiated is an implementation detail but the language makes it impossible to hide. You could say that anyone comparing two Integers by reference is an idiot, but allowing the comparison to begin with is dumber still. – Doval May 7 '15 at 20:43
8

The JLS §4.3.2 defines the class object in the following way:

4.3.2. The Class Object

The class Object is a superclass (§8.1.4) of all other classes.

All class and array types inherit (§8.4.8) the methods of class Object, which are summarized as follows:

  • The method clone is used to make a duplicate of an object.

  • The method equals defines a notion of object equality, which is based on value, not reference, comparison.

  • The method finalize is run just before an object is destroyed (§12.6).

  • The method getClass returns the Class object that represents the class of the object.

  • A Class object exists for each reference type. It can be used, for example, to discover the fully qualified name of a class, its members, its immediate superclass, and any interfaces that it implements.

    The type of a method invocation expression of getClass is Class<? extends |T|> where T is the class or interface searched (§15.12.1) for getClass.

    A class method that is declared synchronized (§8.4.3.6) synchronizes on the monitor associated with the Class object of the class.

  • The method hashCode is very useful, together with the method equals, in hashtables such as java.util.Hashmap.

  • The methods wait, notify, and notifyAll are used in concurrent programming using threads (§17.2).

  • The method toString returns a String representation of the object.

So, that's why equals is in Object but compareTo is in a separate interface. I would speculate that they wanted to keep Object as minimal as possible. They probably figured that nearly all Objects would need equals and hashCode (which is really just a form of equality testing) but not all objects would need to have a concept of ordering, which is what compareTo is used for.

  • I guess there could theoretically be an interface Equitable<T> but if Object implemented it, then every class would be an Equitable<Object>. At that point, is there a difference? In effect not really, in complexity yes. – Captain Man May 7 '15 at 13:56
  • 1
    @CaptainMan In .Net, object has Equals(object), just like in Java, but there's also the IEquatable<T> interface. Though the primary reason for it to exist is to avoid boxing when T is a value type, which is not possible in Java. – svick May 7 '15 at 16:53
  • hashCode is not a form of equality testing, because there are hash collisions. if A and B are equal, they have the same hashCode, but if A and B have the same hashCode, this does not mean they are equal! – Josef May 8 '15 at 8:40
  • actually, your answer would greatly benefit from switching to an older JLS (titanium.cs.berkeley.edu/doc/java-langspec-1.0.pdf) - it has a much better quote on why equals is declared in Object directly: The methods equals and hashCode are declared for the benefit of hashtables such as java.util.Hashtable (§21.7) - as Java design is backwards-compatible, Java 1.0 design choice is the actual reason for equals being where it is. – vaxquis May 17 '16 at 12:53
  • since the edit I've proposed will probably get rejected for being "too drastic", in case you'd want to just Ctrl+C/Ctrl+V the relevant stuff into your answer: pastebin.com/8c4EpLRX – vaxquis May 17 '16 at 13:10
2

In addition to Snowman's excellent answer, remember that Comparable has been a generic interface for a long time. A type doesn't implement compareTo(object), it implements compareTo(T) where T is its own type. This cannot be implemented on object, since object does not know the class that will be derived from it.

object could have defined an compareTo(object) method, but this would have allowed not just what Snowman points out, a comparison between two ArrayList<T>s or between two Threads, but even a comparison between an ArrayList<T> and a Thread. That's even more nonsensical.

0

Suppose I have two object references: X identifies an instance of String holding the content "George"; Y identifies instance of Point holding the coordinates [12,34]. Consider the following two questions:

  • Do X and Y identify equivalent objects?

  • Should X sort before, after, or equivalent to Y?

The fact that X and Y identify instances of unrelated types does not pose any problem when considering the first question. Objects can only be considered equivalent if their types share a common base that defines them as being equivalent; since String and Point have no such base (their only common base type regards all distinct objects as non-equivalent) the answer is simply "no".

The fact that the types are unrelated, however, poses a huge problem with regard to the second question. Some types define ordering relationships among their instances, and some ordering relationships may even extend over multiple types [e.g. it would be possible for BigInteger and BigDecimal to define comparison methods that would allow instances of either type to be ranked relative to instances of the other], but it is not possible in general to take two arbitrary instances and ask "Should X sort before, after, or equivalent to Y" and derive a total ordering. It would be possible to ask "Should X sort before, after, equivalent, or unranked with respect to Y" if objects were required to report a consistent ordering though not a total one, but most sorting algorithms require total orderings. Thus, even if all objects could implement a compareTo method if "unranked" was a valid return, such a method wouldn't be useful enough to justify its existence.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.