2
votes

I have a superclass with 2 variables (int a) (int b) and have a subclass, which extends the functionality of the superclass by overriding 2 superclass methods with improved methods now in the subclass. As part of my subclass I now have a new int (int c) which is a unique id (UUID).

I understand equality is difficult to maintain with the equals method

Can I override the Superclass equals and hashCode methods to show equality AND also do the same for the subclass equals and hashCode method based on my situation described above?

I had initially overriden the equals and hashCode methods in the superclass. If it supposed to be done for the subclass also because of the extra Instance Variable (int c) in the subclass. I understand that there should be a hashCode method to show this, and I read if the haschCode is changed then the equals method of the subclass has to change?

I am really getting confused as to what best to do.

I was wondering if my superclass equals and hashCode methods can show the equality relationships? Is it allowed in conjunction with my superclass equals and hashCode overriden methods that the subclass can show equals comparison for int a, int b and int c and the hashCode methods of the subclass updated to show a unique hashcode for ints a, b and c?

I was thinking of comparing my ints a and b in the superclass equals method and update hashcode for these 2 variables and ints a, b and c in the equals method, updating hashcode for these 3 variable. It's just that int c in the superclass is unique?

Very grateful for any advice here as I believe ignoring and not dealing with the int c in the subclass equals and hashCode methods may be a no no.

Thanks in advance

5
The contract for hashcode and equals says that if a.equals(b) than the hashCode() functions for a and b should return the same value. The converse does not need to be true.Jordan Bentley

5 Answers

1
votes

Basically, you have two possible options:

  1. Ignore c altogether. Define equality for superclass in terms of a and b and don't override it in subclasses.

  2. Always treat instances of superclass and subclass as not equal. It allows you to define equality differently in superclass (based on a and b) and subclass (based on a, b and c).

    Note that overriding equals() and hashcode in subclass is not enough in this case - you also need a special trick when implementing equals() method of superclass, see part about canEquals() method in How to Write an Equality Method in Java.

1
votes

The crux of your question is whether you want it to be possible for an instance of your superclass and an instance of your subclass to compare as equal.

If so, don't override the equals() and hashCode() methods. You might even make them final in the superclass.

If not, then the equals() method of each class should check the type of the argument to equals() using getClass(), not instanceof. For example, in the superclass:

if ((obj == null) || (!getClass().equals(obj.getClass()))
  return false;
/* Compare a and b. */
...

In the subclass:

if (!super.equals(obj))
  return false;
/* Compare c. */
...

If the hash code is based on the a and b fields, you probably won't have to override the hashCode() method, but your class will probably work better as a key if you do account for c in the subclass' implementation. That is, it will put keys with different values of c into different buckets, rather than clumping all instances with the same a and b in the same hash bucket.

0
votes

In order to determine the behavior of hashCode and equals, you need to consider a few scenarios.

class Foo { int a, b; }
class Bar extends Foo { int c;}

Are a Foo and Bar with the same a and b values equal (i.e., do they represent the same value)? If so, then add the equals and hashCode implementation to Foo, and don't override it in Bar. In equals, make sure to check that the object is instanceOf Foo, not that the classes are equal.

Are Bar instances with different c's equal? If so, then don't override Foo's implementation of hashCode and equals.

Otherwise, you should have an implementation of equals and hashCode in both Foo and Bar. Make sure to design it so that Foo instances are only equal to other Foo instances, and Bar instances are only equal to other Bar instances. If not, you could easily violate the symmetry required by equals, e.g

Foo x = new Foo(a,b);
Bar y = new Bar(a,b,c);
x.equals(y); //returns false
y.equals(x); //returns true

The above is very bad, and will cause strange behavior, if for example you put a Foo and a Bar into a Set.

Finally, you should NOT write your own implementation of hashCode and equals for these objects. Most IDEs have an auto-generation tool for these, use that instead.

0
votes

++ to Jordan Bently.

It is generally a good idea to override equals(), toString(), and hashCode() for each class you write. In practice it does not always make sense. But for what you are doing, I would do so. And yes, you can change the implementation in the subclass.

It is good to mark each of your overriden methods with @Override to help provide documentation to the developer(s) that you are overriding. This clues them that you are at least overriding java.lang.Object, if not your super class as well.

0
votes

It depends. Lets say your super class is A and B extends A then this can occur

  • a.equals(a)
  • b.equals(b)
  • a.equals(b) (true)? b.hash == a.hash : hash doesn't matter
  • b.equals(a) (true)? b.hash == a.hash : hash doesn't matter

How should be A and B related? There are two options:

  1. A and B are completely different, therefore a.equals(b) or b.equals(a) is always false. This is the most safe way. You need in equals to test a.getClass() == b.getClass(); to ensure a and b is exactly same type. For example Eclipse IDE generates this kind of equality automatically by default in generate equals() and hashCode().
  2. A and B can be compared, but you can't override it in B to keep contract of equals() and hashCode(). This is forced due symmetric contract (a.equals(b) == b.equals(a)) of equals.