Kilian Foth's answer is excellent. I'd just like to add the canonical example of why this is a problem. Imagine an integer Point class: class Point2D { public int x; public int y; // constructor public Point2D(int theX, int theY) { x = theX; y = theY; } public int hashCode() { return x + y; } public boolean equals(Object o) { if (this == o) { return true; } if ( !(o instanceof Point2D) ) { return false; } Point2D that = (Point2D) o; return (x == that.x) && (y == that.y); } } Now let's sub-class it to be a 3D point: class Point3D extends Point2D { public int x; public int y; public int z; // constructor public Point3D(int theX, int theY, int theZ) { x = theX; y = theY; z = theZ; } public int hashCode() { return x + y + z; } public boolean equals(Object o) { if (this == o) { return true; } if ( !(o instanceof Point3D) ) { return false; } Point3D that = (Point3D) o; return (x == that.x) && (y == that.y) && (z == that.z); } } Super simple! Let's use our points: Point2D p2a = new Point2D(3, 5); Point2D p2b = new Point2D(3, 5); Point2D p2c = new Point2D(3, 7); p2a.equals(p2b); // true p2b.equals(p2a); // true p2a.equals(p2c); // false Point3D p3a = new Point3D(3, 5, 7); Point3D p3b = new Point3D(3, 5, 7); Point3D p3c = new Point3D(3, 7, 11); p3a.equals(p3b); // true p3b.equals(p3a); // true p3a.equals(p3c); // false You are probably wondering why I'm posting such an easy example. Here's the catch: p2a.equals(p3a); // true p3a.equals(p2a); // FALSE! When we compare the 2D point to the equivalent 3D point, we get true, but when we reverse the comparason, we get false (because p2a fails `instanceof Point3D`). There are two morals of this story. 1. It is usually possible to implement a method in a subclass in such a way that it's no-longer compatible with how the super-class expects it to work. 2. It is generally impossible to implement equals() on a significantly different subclass in a way that is compatible with it's parent class. Examples of this abound. Look at Map and SortedMap, Set and SortedSet. When you write a class that you intend to allow people to subclass, it's a really good idea to write out a *contract* for how each method should behave. Even better would be a set of unit tests people could run against their implementations of overridden methods. Nobody does that because it takes too long. But if you care, that's the thing to do. A great example of a well-spelled out contract is [Comparator][1]. Just ignore what it says about `.equals()` for the reasons described above. #Bonus Comparator is an example of the Strategy design pattern. The typeclasses that Haskell and Scala people get excited about are also the Strategy pattern. The Strategy pattern can solve the issues of inheritance and breaking the contracts of inherited methods. Using a Comparator doesn't have any of the problems that equals() has, because you can always swap one Comparator for another. Inheritance isn't bad or wrong, it's just tricky. For further reading, check out Philip Wadler's paper [How to make ad-hoc polymorphism less ad hoc][2] [1]: https://docs.oracle.com/javase/8/docs/api/ [2]: http://www.cse.iitk.ac.in/users/karkare/courses/2010/cs653/Papers/ad-hoc-polymorphism.pdf