ohbrobig
ohbrobig

Reputation: 966

Why should the parameter of an equals() method of a new class be of type Object?

I am rereading Building Java Programs by Stuart Reges and noticed something which I don't quite understand. Its in regards to overloading of the equals() method in any new class. Lets say we define a class as such:

public Point{  
   private int x;
   private int y;

   public Point(int x, int y){
       this.x = x;
       this.y = y;
   }

   public getX(){
       return this.x;
   }

   public getY(){
       return this.y;
   }
}

The book is suggesting that any time we define a new class, the equals() method we define for a new class should be written as such:

public boolean equals(Object o) {
     if (o instanceof Point) {
          Point other = (Point) o;
          return x == other.x && y == other.y;
      } else { 
          return false;
      }
}

Why should the equals method accept objects of the generic type "Object" and not of type "Point"? The book says if the equals method header doesn't match the equals method header of the generic Object class it won't be overloaded (and I get that it won't be overloaded, otherwise). However, this is kind of nonintuitive because the only time when they could actually be equal is if are of the same type...

When I pass a String object as a parameter to my equals() method which accepts parameters of type Point, and not String, it correctly returns false. Won't the generic equals() method (which compares memory addresses) fit the bill whenever we pass different types?

Upvotes: 5

Views: 2309

Answers (4)

Jameson
Jameson

Reputation: 6659

Consider a case like this:

public class BoatWeight {
    float weightTonnes;
    public float getWeightTonnes() {
        return weightTonnes;
    }
}

public class CarWeight {
    int weightLb;
    public int getWeightLb() {
        return weightLb;
    }
}

They are Comparable. But only if you define the logic:

public class CarWeight {
    ...
    public boolean equals(Object o) {
         if (o instanceof BoatWeight) {
             final int boatWeightLb =
                 WeightConverter.tonnesToLb(o.getWeightTonnes());
             return  boatWeightLb == this.getWeightLb();
         } else if (o instanceof CarWeight) {
             return this.getWeightLb() == o.getWeightLb();
         }
    }
    ...
}

The above could obviously be cleaned up by using a common interface and the same units of measure, and then using it as the type to equals(). But what if you're using classes from a library? You don't get to do that. Object is the only interface common across all ... objects.

Upvotes: 1

Stefan Haustein
Stefan Haustein

Reputation: 18803

Why not have both -- saves the instanceof check where the right overload can be determined statically, but still overloads the base method properly, so it works when storing points in collections etc.:

@Override
public boolean equals(Object o) {
  return (o instanceof Point) ? equals((Point) o) : false;
}

public boolean equals(Point other) {
  return x == other.x && y == other.y;
}

Upvotes: 1

Joe C
Joe C

Reputation: 15704

Not necessarily true. There are often use cases where equals can represent two objects with equal values even though they are from two different classes.

For example, consider an ArrayList (or any other subclass of AbstractList). By its semantics, two Lists are considered equal if they have the same elements in the same order. Thus, it is possible for an ArrayList to be equal to any type of List, be it a LinkedList, another ArrayList, or even my own implementation of List.

Upvotes: 5

Edd
Edd

Reputation: 1370

It's because you are overriding the

boolean equals(Object o){}

defined in the Object class. It has to be generic enough for any Object as it's defined at the 'inheritance root'.

Upvotes: 0

Related Questions