Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Equals for extended object wants to check super.equals(), should this be composition instead of inheritance if I do not want this check [closed]

So the scenario is as follows:

class Feline  
{  
   String name;  
   int age;  

   equals(Object obj) {...}  
   hashCode(){...}  
}  


class Cat extends Feline  
{  
   int teeth;  

   hashCode(){...}  
   equals(Object obj)  
   {
     if (!super.equals(obj))
    {
        return false;   //If I don't want this should I use 
    }    
      ...
   }  
}

The issue is in reality this inheritance is correct, but to the program it is not necessarily true. My thought on this is Cat should actually be composed of a Feline object. The question is, which of these approaches should I take?

EDIT
This is the implementation from Eclipse, default equals/hashcode. It may be that the implementation of equals is not the most accurate way to do this.

like image 458
Woot4Moo Avatar asked Feb 22 '23 16:02

Woot4Moo


1 Answers

Ohoh equality checking in the face of inheritance. That's EXTREMELY hard to get right and rather long to describe.

The correct solution isn't as straight forward as one would think, so please go read this - that should clear up all your questions. If not feel free to ask again :)

Edit: So as a short summary of the above link: An equality method should fulfill the following properties:

  • It is reflexive: for any non-null value x, the expression x.equals(x) should return true.
  • It is symmetric: for any non-null values x and y, x.equals(y) should return true if and only if y.equals(x) returns true.
  • It is transitive: for any non-null values x, y, and z, if x.equals(y) returns true and y.equals(z) returns true, then x.equals(z) should return true.
  • It is consistent: for any non-null values x and y, multiple invocations of x.equals(y) should consistently return true or consistently return false, provided no information used in equals comparisons on the objects is modified.
  • For any non-null value x, x.equals(null) should return false.

To guarantee that this works, we need to specify another method: public boolean canEqual(Object other). This method should return true if the other object is an instance of the class in which canEqual is (re)defined, false otherwise.

In other words the method must always be overwritten if we overwrite equal() itself. The implementation itself is trivial, a short example:

class Foo {
    public boolean canEqual(Object other) {
        return other instanceof Foo;
    }
    // equals implementation, etc.
}

The equals method itself must always first check if the given object canEqual itself, e.g. something along the lines of other.canEqual(this) && restOfComparison.

Short example of a class extending the above Foo:

class Bar extends Foo {
    public boolean equals(Object other) {
        if (other instanceof Bar) {
             Bar that = (Bar)other;
             return that.canEqual(this) && otherStuff;
        }            
        return false;
    }

    public boolean canEqual(Object other) {
          return other instanceof Bar;
    }
}
like image 177
Voo Avatar answered May 01 '23 06:05

Voo