1

Say I have a class where I would have to check for multiple different types of objects. Would it be feasible/possible to override equals() in the following manner?

public boolean equals(Object o){
    if(o == null) return false;
    if(o instanceof class1){
        return this.class1.equals((class1) o);
    if(o instanceof class2){
        return this.class2.equals((class2) o);
    ...

}

Would this ever be useful? I assume here that I create a static overloaded equals() method in the respective classes (though maybe polymorphism would take care of that automatically as long as I cast?).

filpa
  • 3,651
  • 8
  • 52
  • 91
  • 3
    it can be "useful" depending on what you're doing, but you have to know that having *equals* and *hashCode* at the top of Java's hierarchy is purely and simply flawed. There's no way around it. You can't guarantee the equals/hashcode contracts as soon as you have inheritance. As simple as that. **Impossible**. It's nicely and very clearly and indisputably explained by Joshua Bloch in *Effective Java*. There's also a great talk by Martin Odersky (from Scala fame) on that subject at Artima. – Cedric Martin Oct 28 '11 at 09:21
  • 1
    That code is legal, so it is possible. But it is not a good idea to say that two objects are equal if they are of the same class. If you are determined to implement such an experiment, the proper way of doing it in OO is to override equals in each subclass. – Mister Smith Oct 28 '11 at 09:55
  • @CedricMartin Not true, you can. You just can't add attributes to subclasses that change equality. Well and even the previous sentence is wrong: You can implement equals correctly (ie without violating any of the guaranteed mathematical properties of an equality relation) even then, it's just generally not what anyone would want - so it has little practical value. Actually odersky wrote an article showing how to do that. Edit: Or are you just saying that you can't guarantee that the subclasses implement it correctly? True, but then the same holds for lots of things I'd say. – Voo Oct 29 '11 at 03:20
  • @Voo: Odersky wrote an article showing one day to work around the problem, *without using equals() and hashCode()* (he's creating other methods). Joshua Bloch said this: *"There is simply no way to extend an instantiable class and add an aspect while preserving the equals contract"*. So I stand by my assertion that if you want to use inheritance (which, yes, typically means having classes and subclasses) then *equals* and *hashCode* at the top of the OO hierarchy in Java are broken. I don't dispute there are workaround: I simply state that *equals* and *hashCode* should not have been there. – Cedric Martin Oct 29 '11 at 12:07
  • @Voo: now I don't dispute that if you don't do OO, for example if you use Java in a procedural way and always put the exact same kind of objects in collections, then *equals* and *hashCode* do work : ) So it boils down to: *"you can if you don't add aspect/attributes to subclasses"* but then are we really talking about inheritance and OO!? – Cedric Martin Oct 29 '11 at 12:10
  • @CedricMartin Odersky is still using equals, but with a helper method. And sure I've written subclasses that don't change behavior in such a way and work fine. And even situations where I added attributes and was fine with the subclass always evaluating non equal when compared to its parent. But then how would you solve the underlying problem? I don't see how it matters where you implement equals - as soon as you compare a super with a subclass those problems just creep up. What would be the proposed solution? – Voo Oct 29 '11 at 12:45
  • @Voo: *"And even situations where I added attributes and was fine with the subclass always evaluation non equal"* but then you broke transivity (*Effective Java*). And so it's full circle, I'll state once again the exact sentence Joshua Bloch wrote in Effective Java: *"There is simply no way to extend an instantiable class and add an aspect while preserving the equals contract"*. You can argue with Joshua Bloch as much as you want: you **did break the equals contract**. Proposed solution? Admit that *equals* and *hashCode* were broken, stop using them or stop pretending you're doing OO. – Cedric Martin Oct 29 '11 at 13:40
  • @Voo: you're also conveniently forgetting to point out that Odersky states his solution ain't perfect (and it's not done using a helper method, it's done using a method that should be present **in every single object where equals and hashCode are present**). Now stop it! – Cedric Martin Oct 29 '11 at 13:41
  • @CedricMartin No we **don't** break transitivity by that, EVERY mathematical property an equals relation has to have can be guaranteed. Yes we need an additional method whenever we overwrite equals, yes the solution is not necessarily intuitive - and? If your solution is "Well just don't use equals" - I think I'll go with the "not perfect solution" instead of not using anything at all. Must be fun to program in real OOP land without hashmaps, ordered collections and anything else where you need the two methods. I think I prefer the practical solution. – Voo Oct 29 '11 at 16:18
  • See also http://stackoverflow.com/questions/596462/any-reason-to-prefer-getclass-over-instanceof-when-generating-equals – Raedwald Apr 15 '15 at 20:01

4 Answers4

3

I very much doubt that because you can easily violate the transitivity of equals this way (x.equals(y) && x.equals(z) implies y.equals(z))

unless of course you do a very throughout setup involving all classes but that would be a pain to get right

ratchet freak
  • 47,288
  • 5
  • 68
  • 106
1

Semantically I don't think this is a good idea. Equals should not be based on class types but instance variables. Apart from that, an Apple should never be equal to a Pear but only to other apples.

zeller
  • 4,904
  • 2
  • 22
  • 40
1

It seems like you want a method to check whether a specified object is equal to at least one of various member variables in the receiver.

It is inappropriate to override java.lang.Object#equals for this purpose.

You should instead provide a different method, e.g.

public boolean hasEquals(Object o) { // your impl }
ewan.chalmers
  • 16,145
  • 43
  • 60
0

If you're sure that it won't lead to mutual recursion you can just return that.equals(this). However in general object equality implies either equality of classes, or at least a coherent inheritance relationship between them.

user207421
  • 305,947
  • 44
  • 307
  • 483