4

It's clear that equals() (and of course hashCode()) are valuable when you're dealing with immutable value objects -- map keys, strongly typed field values that you need to compare across the objects that contain them, etc.

But apart from value objects, how often is it really likely that you'll have two independently constructed instances and want them to be equal?

It's hard for me to imagine a realistic scenario where referential equality wouldn't, de facto, get you what you want; and in those specific cases, it seems like a scenario-specific equivalence method (isEquivalentTo(Foo) rather than equals(Object)) would be safer. Particularly for mutable objects?

What's the use case for equals() in non-value types?

David Moles
  • 48,006
  • 27
  • 136
  • 235
  • 2
    I sometimes override them in a base class and make them `final` to ensure all classes of the type have the same semantics as `Object.equals()` and `Object.hashCode()`. That wasn't quite what you were asking about :-) – Raedwald Feb 01 '12 at 01:49
  • @Raedwald So basically what you're saying is you make them final to make sure somebody else doesn't come along and override `Object`'s default behavior later? :) – David Moles Feb 01 '12 at 17:44
  • I support this kind of defensive programming. :) – David Moles Feb 01 '12 at 18:58
  • It's distinctly possible there isn't one (not one that really matters at least). But how would you have redesigned Java's core object hierarchy to reflect `equals()` should be `final` for a certain category of objects but overridable for the complementary category? – FoolishSeth Nov 26 '12 at 08:39
  • Good question. I'm not a language designer, but my first impulse is to say the solution would have something to do with making immutability a top-level language concept, and separating the concepts of identity and state (as Clojure, for instance, [makes a point of doing](http://clojure.org/state)). – David Moles Nov 27 '12 at 23:20

2 Answers2

2

Um. Set<E> is mutable, and has a (useful) definition of equals(). That doesn't seem useless...

Louis Wasserman
  • 191,574
  • 25
  • 345
  • 413
  • 1
    The collections equality methods are kind of useful. I use them every now and then -- usually when I'm being lazy in a unit test assertion. But not often. They're not as useful as stuff like `containsAll()`, and it's not obvious to me why they were a good idea. – David Moles Feb 01 '12 at 01:08
  • 1
    Well, for starters, when sets are immutable -- either through (careful) use of `Collections.unmodifiableSet`, or through Guava's `ImmutableSet`, or the like -- you can use them as keys in a map, or whatever you like. It's a fair point that most of the time, having an `equals()` method for mutable types isn't especially useful, but...if you need to test equivalence, then, well, why _not_ just override `equals` instead of creating your own `isEquivalentTo` method? – Louis Wasserman Feb 01 '12 at 01:11
  • For an immutable class there's no harm in overriding `equals()` (and I can see how it could be useful for, e.g., sublist or subtree sharing in an immutable collections framework). For a mutable class, `isEquivalentTo()` doesn't open you up to any of the pitfalls of other classes or standard libraries -- like the collections framework -- assuming `equals()` and `hashCode()` are stable. – David Moles Feb 01 '12 at 01:16
  • That was, clearly, a decision that the usefulness of allowing e.g. `Set` to be used as a map key outweighed the risks. I think it was the right decision. Usage of e.g. technically mutable map keys is rare enough that it's not a major risk. – Louis Wasserman Feb 01 '12 at 01:21
0

From docs.oracle.com:

Interface List<E>

boolean contains(Object o)

Returns true if this list contains the specified element. 

More formally, returns true if and only if this list contains at least one element e such that (o==null ? e==null : o.equals(e)).

So, overriding equals is useful for this (and other) language constructs.

Consider this (sorry, it's in C#):

class MimeType
{
    public string Name { get; set; }
    public string Extension { get; set; }    

    public override bool Equals(object obj)
    {
        var other = obj as MimeType;
        if (other == null) return false;
        return other.Name == Name;
    }
}

class Program
{
    void Main()
    {
        var jpeg1 = new MimeType();
        jpeg1.Name = "image/jpeg";
        jpeg1.Extension = ".jpg";

        var jpeg2 = new MimeType();
        jpeg2.Name = "image/jpeg";
        jpeg2.Extension = ".jpeg";    

        var list = new List<MimeType>();
        list.Add(jpeg1);

        if (!list.Contains(jpeg2))
            list.Add(jpeg2);
    }
}

In the above example, the jpeg2 object will not be added to the list, because the list already contains an equivalent instance.

Update:

Just for fun, I added the Extension member to the MimeType class.

qxn
  • 17,162
  • 3
  • 49
  • 72
  • 2
    Yes, that would be an example of a value type. And I sure hope it's immutable! :) – David Moles Feb 01 '12 at 01:18
  • I updated my little example. Would the addition of Extension make the MimeType class a mutable type? That's not to say they are still equivalent, but I can imagine some cases in which it still may be desirable to consider the two instances equal. – qxn Feb 01 '12 at 01:23
  • That's what I'm asking: what are those cases you can imagine? – David Moles Feb 01 '12 at 17:40