I was writing some Java test code when I faced up the voracity of the method. It presents, despite its ostensible simplicity, a tricky problem. equals I'd like to emphasize that this is not a specific Java issue. For example C# has a way. homologous A bit of Java context The class is the root of every class. It defines various methods and is one of them. By default this method has a simple behavior: an object x is only equals to itself. Any other object is different. Object equals Obviously the method has the common properties of an equivalence relation. It is : x is equal to x; It is : if x is equal to y, then y is equal to x; and so on... equals reflexive symmetric Furthermore a logic relationship links and the method. The latter returns the object hash. In this context it's an integer representation of the object. So if two object are equal, then their hash should be equal too. equals hashCode The problem I'll use a simple code example to highlight the main issue. Here is the starting point: { ; ; } { ... } interface Book String title () String author () final class DbBook implements Book We can ask to a instance its title and its author. A instance represents a book stored in a database. Book DbBook As I said the class is the root of every class. This means that also inherits the method with its default behavior. So we should override to implement a custom equivalence relation. To respect the aforesaid logic implicatio we should also override the method. Object DbBook equals equals hashCode Now suppose that in our context two books are equal if they have the same title. This seems the goal and here is a common implementation (I generated it with the IDE): equals { ... { ( == o) ; (!(o DbBook)) ; DbBook dbBook = (DbBook) o; title().equals(dbBook.title()); } { Objects.hash(title()); } ... } final class DbBook implements Book @Override public boolean equals ( Object o) final if this return true if instanceof return false final return @Override public int hashCode () return However because the check, is only comparable to . This means that an instance is always different from , though having the same title. instanceof aDbBook anotherDbBook AnotherBookImplementation aDbBook The problem seems the check. So we can weak it a bit: instanceof { ... { ( == o) ; (!(o Book)) ; Book book = (Book) o; title().equals(book.title()); } ... } final class DbBook implements Book @Override public boolean equals ( Object o) final if this return true if instanceof return false final return In this way we are restricting to be a instance. o Book The effect of this change is the of our software. As I anticipated the method should be reflexive. This means that implementation must exhibit this behavior. In other words: every implementation is with each other. Definitely it's a really bad approach. destruction equals every Book equals Book high-coupled A different approach The main issue of the approach is that responsabilities aren't decoupled correctly. There should be another object responsible of the comparison. There should be another object that the comparison. equals represents There may be various implementation of this approach. I'll suggest one called and another . representation-based behavior-based Representation-based equality The first one is derives from this . Basically an object (like ) can gives us a representation of itself. Then a object represents a comparison between two representation. In this way a representation is similar to the hash returned by . But it's more generic because it could be based on bytes, strings and so on... article aDbBook Comparison<R> R hashCode However this means that could be equal to , if they have the same representation. I consider this as the main drawback of this approach. aCat aDog Behavior-based equality The is born from an observation. I think that the only valid discriminating factor about objects is their behavior. It's exposed through the methods. Or, more formally, through the messages the object supports. The protocol or interface is the collection of the supported messages. behavior-based For this reason the first step to define equality should be based on interfaces. Then an object will represent the equality between two objects with the same interface. Equality In this way will be always different from because the different interfaces. Presumably the former implements a interface, the latter a interface. Nonetheless, thanks to polymorphism, if they both implement a interface, then they could be equal. This could be possibile with limited to the interface. aCat aDog Cat Dog Pet anEquality Pet Here is an example related to the initial example. I defined two classes to stress out that equality is not a responsability. Book Equality Book { ; } { TitleBasedEquality( Book book, Book anotherBook) { .book = book; .anotherBook = anotherBook; } { book.title().equals(anotherBook.title()); } Book book; Book anotherBook; } { PrefixBasedEquality( Book book, Book anotherBook, Integer length) { .book = book; .anotherBook = anotherBook; .length = length; } { first = book.title().substring(length); second = anotherBook.title().substring(length); first.equals(second); } Book book; Book anotherBook; Integer length; } interface Equality Boolean equals () final class TitleBasedEquality implements Equality final final this this @Override Boolean public equals () return private final private final final class PrefixBasedEquality implements Equality final final final this this this @Override Boolean public equals () var var return private final private final private final So a object compares the full title. A compares only a prefix. TitleBasedEquality PrefixBasedEquality We gather a lot of flexibility. And we can choose the correct equality comparison based on the context. This is possible thanks to the responsability decoupling. However, as you can see, I'm using . I could replace it with . But I consider this case as a reasonable compromise forced by the programming language. String.equals StringEquality A possible drawback of this approach regards an interface with only void methods. In this case each pair of instances are always equal. But this means that these type of objects are only and always comparated on their interface. I find it coherent and I find it respectful towards the objects. Conclusion Definitely the object equality problem is a tough problem. I think that the major issue is that we think equality in terms of data. But objects are not data. This is the reason because I support the idea of some sort of behavior-based comparison. After all the exhibited behavior is what distinguishes one object from another one. Nothing more.