Dylan Watson
Dylan Watson

Reputation: 2323

Distinguish between subclasses via equals & hashcode

I am looking for guidance with regard to overriding both hashcode and equals in a subclass.

I have found a similar question here: Overriding equals() & hashCode() in sub classes ... considering super fields

But what I want is something slightly different.

Imagine this (somewhat stupid) example:

class Food {

  String name;

  @Override
  public boolean equals(Object obj) {
    if (obj instanceof Food) {
      Food other = (Food)obj;
      return name.equals(other.name);
    }
    return false;
  }

  @Override
  public int hashCode() {
    return name.hashCode();
  }
}

class Vegetable extends Food {
  // No additional fields here
  // Some methods here
}

class Fruit extends Food {
  // No additional fields here
  // Some methods here
}

Given that:

  1. The subclasses do not add any extra fields
    • in this example at least, they are basically just marker classes
  2. A Fruit and Vegetable with the same name, should not be equal

Questions:

  1. Would you expect the subclass equals to simply contain an instanceof check for the subclass and a call to super.equals?
  2. How should the hashcode be structured in an attempt to have Fruit and Vegetable instances with the same name have different hashcodes?

Upvotes: 1

Views: 759

Answers (2)

fge
fge

Reputation: 121780

You can just use .getClass() in Foo's .equals():

@Override
public final boolean equals(final Object obj)
{
    if (obj == null)
        return false;
    if (this == obj)
        return true;
    // Class<?> are signletons, therefore we can use ==/!=
    if (getClass() != obj.getClass())
        return false;
    return name.equals(((Foo) obj).name);
}

.getClass() will return the class of the object, therefore Fruit or Vegetable, or even Food.

For .hashCode(), name.hashCode() is fine. Don't forget that there is no requirement that the hashcode of two objects which are not .equals() be different. But if you want to, then .getClass() can be used here as well:

@Override
public final int hashCode()
{
    return 31 * getClass().hashCode() + name.hashCode();
}

Upvotes: 2

Radiodef
Radiodef

Reputation: 37855

  1. Would you expect the equals to simply contain an instanceof check and a call to super.equals?

instanceof is dangerous here because Food is not abstract. This would mean equals was not symmetric.

someFruit.equals(someFood) // will be false
someFood.equals(someFruit) // will be true

This may not turn in to a problem but it's a thing you should consider.

If you don't want to break the contract, this is a case where Food should check whether this.getClass() == obj.getClass(). If you do that then you also don't necessarily need to override it in the subclasses.

Otherwise it doesn't really matter. The method is defined contractually and you can implement it however you want.

  1. How should the hashcode be structured in an attempt to have Fruit and Vegetable instances with the same name have different hashcodes?

They don't need to be different.

Upvotes: 1

Related Questions