Right way to implement equals contract [duplicate] - java

This question already has answers here:
What issues should be considered when overriding equals and hashCode in Java?
(11 answers)
Closed 7 years ago.
I have a domain object called User. Properties of user include ssoId, name, email, createdBy, createdDate and userRole. Of these, ssoId must be unique in the sense no two users can have the same sso id. So my equals method checks for the sso id and returns either true or false.
#Override public boolean equals(Object o) {
if (!(o instanceof User))
return false;
return user.getSsoId().equals((User)o.getSsoId());
}
What I feel is that this is an incorrect implementation, though it is correct as far as the business rules are concerned. The above implementation will return true for two objects with same sso id but with different values for say name or email or both. Should I change my equals contract to check the equality of all fields? What is your suggestion?

This is (almost) correct for "technical equality", but not for "natural equality". To achieve top technical equality, you should also test the reflexive o == this. It may happen that the object isn't persisted in DB yet and thus doesn't have a technical ID yet. E.g.
public class User {
private Long id;
#Override
public boolean equals(Object object) {
return (object instanceof User) && (id != null)
? id.equals(((User) object).id)
: (object == this);
}
#Override
public int hashCode() {
return (id != null)
? (User.class.hashCode() + id.hashCode())
: super.hashCode();
}
}
For "natural equality" you should rather compare all non-technical properties. For "real world entities" this is after all more robust (but also more expensive) than technical equality.
public class User {
private String name;
private Date birth;
private int housenumber;
private long phonenumber;
#Override
public boolean equals(Object object) {
// Basic checks.
if (object == this) return true;
if (!(object instanceof User)) return false;
// Property checks.
User other = (User) object;
return Objects.equals(name, other.name)
&& Objects.equals(birth, other.birth)
&& (housenumber == other.housenumber)
&& (phonenumber == other.phonenumber);
}
#Override
public int hashCode() {
return Objects.hash(name, birth, housenumber, phonenumber);
}
}
True, that's lot of code when there are a lot of properties. A bit decent IDE (Eclipse, Netbeans, etc) can just autogenerate equals(), hashCode() (and also toString(), getters and setters) for you. Take benefit of it. In Eclipse, rightclick code and peek the Source (Alt+Shift+S) menu option.
See also:
JBoss: Equals and HashCode (in view of persistence)
Hibernate: Persistent Classes - implementing equals() and hashCode()
Related SO question: Overriding equals and hashCode in Java

If in your model ssoid must be unique, that implies that the values for the other fields should not be different for two instances of User. If you want to validate that assumption, you could do so with assertions within the equals method if the overhead is not an issue.

What you're doing seems fine, and you're not violating any of the rules that equals must follow.
You may still want to check other fields, not to change equals's semantics, but to detect an inconsistency in your business logic, and possibly trigger an assertion/exception.

This is a tricky decision to make.
This is a spot i got into when considering hashing a few months ago. I would suggest you read up on what a hash is because it is highly relevant to your answer ... i suggest that you are looking to implement some kind of hash and test its equality.
There are different kinds of equality ... there is the equality of the identity of the object, the equality of the data of the object, the equality of the entire object ... you could also include audit information in there also.
The fact is that 'equal' has many possible meanings.
I resolved this by implementing equal as a strict equality across all fields simply because after asking around it seems to be the intuitive meaning of equals. I then constructed methos for the other kinds of equality i required and defined an interface to wrap these.
I wouldnt test equality on object == this because often you are testing two different objects with the same data which in my book are equal despite them referring to different memory addresses.

Related

Using Primiry Key (id) while overriding equals() and hashCode() methods

I am really too confused with the equals() and hashCode() methods after reading lots of documentation and articles. Mainly, there are different kind of examples and usages that makes me too confused.
So, could you clarify me about the following points?
1. If there is not any unique field in an entity (except from id field) then should we use getClass() method or only id field in the equals() method as shown below?
#Override
public boolean equals(Object o) {
if (this == o) return true;
if (getClass() != o.getClass()) return false;
// code omitted
}
2. If there is a unique key e.g. private String isbn;, then should we use only this field? Or should we combine it with getClass() as shown below?
#Override
public boolean equals(Object o) {
if (this == o) return true;
if (getClass() != o.getClass()) return false;
Book book = (Book) o;
return isbn == book.isbn;
}
3. What about NaturalId? As far as I understood, it is used for unique fields e.g. private String isbn;. What is the purpose of its usage? Is it related to equals() and hashCode() methods?
It all boils down to what your class actually represents, what is its identity and when should the JVM consider two objects as actually the same. The context in which the class is used determines its behavior (in this case - equality to another object).
By default Java considers two given objects "the same" only if they are actually the same instance of a class (comparison using ==). While it makes sense in case of strictly technical verification, Java applications are usually used to represent a business domain, where multiple objects may be constructed, but they should still be considered the same. An example of that could be a book (as in your question). But what does it mean that a book is the same as another?
See - it depends.
When you ask someone if they read a certain book, you give them a title and the author, they try to "match" it agains the books they've read and see if any of them is equal to criteria you provided. So equals in this case would be checking if the title and the author of a given book is the same as the other. Simple.
Now imagine that you're a Tolkien fan. If you were Polish (like me), you could have multiple "Lord of the Rings" translations available to read, but (as a fan) you would know about some translators that went a bit too far and you would like to avoid them. The title and the author is not enough, you're looking for a book with a certain ISBN identifier that will let you find a certain edition of the book. Since ISBN also contains information about the title and the author, it's not required to use them in the equals method in this case.
The third (and final) book-related example is related to a library. Both situations described above could easily happen at a library, but from the librarian point of view books are also another thing: an "item". Each book in the library (it's just an assumption, I've never worked with such a system) has it's own identifier, which can be completely separate from the ISBN (but could also be an ISBN plus something extra). When you return a book in the library it's the library identifier that matters and it should be used in this case.
To sum up: a Book as an abstraction does not have a single "equality definition". It depends on the context. Let's say we create such set of classes (most likely in more than one context):
Book
BookEdition
BookItem
BookOrder (not yet in the library)
Book and BookEdition are more of a value object, while BookItem and BookOrder are entities. Value objects are represented only by their values and even though they do not have an identifier, they can be equal to other ones. Entities on the other hand can include values or can even consist of value objects (e.g. BookItem could contain a BookEdition field next to its libraryId field), but they have an identifier which defines whether they are the same as another (even if their values change). Books are not a good example here (unless we imagine reassigning a library identifier to another book), but a user that changed their username is still the same user - identified by their ID.
In regard to checking the class of the object passed to the equals method - it is highly advised (yet not enforced by the compiler in any way) to verify if the object is of given type before casting it to avoid a ClassCastException. To do that instanceof or getClass() should be used. If the object fulfills the requirement of being of an expected type you can cast it (e.g. Book other = (Book) object;) and only then can you access the properties of the book (libraryId, isbn, title, author) - an object of type Object doesn't have such fields or accessors to them.
You're not explicitly asking about that in your question, but using instanceof and getClass() can be similarly unclear. A rule of thumb would be: use getClass() as it helps to avoid problems with symmetry.
Natural IDs can vary depending on a context. In case of a BookEdition an ISBN is a natural ID, but in case of just a Book it would be a pair of the title and the author (as a separate class). You can read more about the concept of natural ID in Hibernate in the docs.
It is important to understand that if you have a table in the database, it can be mapped to different types of objects in a more complex domain. ORM tools should help us with management and mapping of data, but the objects defined as data representation are (or rather: usually should be) a different layer of abstraction than the domain model.
Yet if you were forced to use, for example, the BookItem as your data-modeling class, libraryId could probably be an ID in the database context, but isbn would not be a natural ID, since it does not uniquely identify the BookItem. If BookEdition was the data-modeling class, it could contain an ID autogenerated by the database (ID in the database context) and an ISBN, which in this case would be the natural ID as it uniquely identifies a BookEdition in the book editions context.
To avoid such problems and make the code more flexible and descriptive, I'd suggest treating data as data and domain as domain, which is related to domain-driven design. A natural ID (as a concept) is present only on the domain level of the code as it can vary and evolve and you can still use the same database table to map the data into those various objects, depending on the business context.
Here's a code snippet with the classes described above and a class representing a table row from the database.
Data model (might be managed by an ORM like Hibernate):
// database table representation (represents data, is not a domain object)
// getters and hashCode() omitted in all classes for simplicity
class BookRow {
private long id;
private String isbn;
private String title;
// author should be a separate table joined by FK - done this way for simplification
private String authorName;
private String authorSurname;
// could have other fields as well - e.g. date of addition to the library
private Timestamp addedDate;
#Override
public boolean equals(Object object) {
if (this == object) {
return true;
}
if (object == null || getClass() != object.getClass()) {
return false;
}
BookRow book = (BookRow) object;
// id identifies the ORM entity (a row in the database table represented as a Java object)
return id == book.id;
}
}
Domain model:
// getters and hashCode() omitted in all classes for simplicity
class Book {
private String title;
private String author;
#Override
public boolean equals(Object object) {
if (this == object) {
return true;
}
if (object == null || getClass() != object.getClass()) {
return false;
}
Book book = (Book) object;
// title and author identify the book
return title.equals(book.title)
&& author.equals(book.author);
}
static Book fromDatabaseRow(BookRow bookRow) {
var book = new Book();
book.title = bookRow.title;
book.author = bookRow.authorName + " " + bookRow.authorSurname;
return book;
}
}
class BookEdition {
private String title;
private String author;
private String isbn;
#Override
public boolean equals(Object object) {
if (this == object) {
return true;
}
if (object == null || getClass() != object.getClass()) {
return false;
}
BookEdition book = (BookEdition) object;
// isbn identifies the book edition
return isbn.equals(book.isbn);
}
static BookEdition fromDatabaseRow(BookRow bookRow) {
var edition = new BookEdition();
edition.title = bookRow.title;
edition.author = bookRow.authorName + " " + bookRow.authorSurname;
edition.isbn = bookRow.isbn;
return edition;
}
}
class BookItem {
private long libraryId;
private String title;
private String author;
private String isbn;
#Override
public boolean equals(Object object) {
if (this == object) {
return true;
}
if (object == null || getClass() != object.getClass()) {
return false;
}
BookItem book = (BookItem) object;
// libraryId identifies the book item in the library system
return libraryId == book.libraryId;
}
static BookItem fromDatabaseRow(BookRow bookRow) {
var item = new BookItem();
item.libraryId = bookRow.id;
item.title = bookRow.title;
item.author = bookRow.authorName + " " + bookRow.authorSurname;
item.isbn = bookRow.isbn;
return item;
}
}
If there is not any unique field in an entity (except from id field) then should we use getClass() method or only id field in the
equals() method as shown below?
#Override
public boolean equals(Object o) {
if (this == o) return true;
if (getClass() != o.getClass()) return false;
// code omitted
}
we achieve two following goals when comparing classes in #equals implementation:
thus we make sure that we do not compare apples with oranges (it could be correct though)
the code you omitted must perform cast of Object o to some known class, otherwise we will unable to extract required information from Object o, so, we make #equals method safe - nobody expect to get ClassCastException when calling Set#add for example. Using instanceof there seems not to be a good idea because it violates symmetric and transitive contracts of equals.
Also it is worth noticing that calling o.getClass() could cause unexpected behaviour when Object o is a proxy, some people prefer to either call Hibernate.getClass(o) instead or implement other tricks.
I am really too confused with the equals() and hashCode() methods
after reading lots of documentation and articles. Mainly, there are
different kind of examples and usages that makes me too confused
If there is a unique key e.g. private String isbn;, then should we use > only this field? Or should we combine it with getClass() as shown below?
#Override
public boolean equals(Object o) {
if (this == o) return true;
if (getClass() != o.getClass()) return false;
Book book = (Book) o;
return isbn == book.isbn;
}
That is very controversial topic, below are some thoughts on the problem:
it is a good idea to maintain PK column for each DB table - it costs almost nothing, but simplifies a lot of things - imagine someone asked you to delete some rows and instead of delete from tbl where id=... you need to write delete from tbl where field1=... and field2=... and ...
PK's should not be composite, otherwise you might get surprised with queries like select count(distinct field1, field2) from tbl
the argument that entities get their IDs only when get stored in DB that is why we can't rely or surrogate ids in equals and hashCode is just wrong, yes, it is a common situation/behaviour for the most JPA projects, but you always has an option to generate and assign IDs manually, some examples below:
EclipseLink UserGuide: "By default, the entities Id must be set by the application, normally before the persist is called. A #GeneratedValue can be used to have EclipseLink generate the Id value." - I believe it is clear enough that #GeneratedValue is just an extra feature and nobody prevents you from creating own object factory.
Hibernate User Guide: "Values for simple identifiers can be assigned, which simply means that the application itself will assign the value to the identifier attribute prior to persisting the entity."
some popular persistent storages (Cassandra, MongoDB) do not have out-of-the-box auto-increment functionality, however nobody may say those storages do not allow to implement some high level ideas like DDD, etc.
in such discussions examples make sense but book/author/isbn is not the good one, below are something more practical: my db contains about 1000 tables, and just 3 of them contains something similar to natural id, please give me the reason why I should not use surrogate ids there
it is not always possible to use natural ids even when they exist, some examples:
bank card PAN - it seems to be unique, however you must not even store it in DB (I believe SSN, VIN are also security sensitive)
no matter what anyone says, thinking that natural ids never change is too naive, surrogate ids never change
they may have bad format: too long, case insensitive, contains unsafe symbols, etc
it is not possible to implement soft deletes feature when we are using natural ids
PS. Vlad Mihalcea had provided amusing implementation of hashCode:
#Override
public boolean equals(Object o) {
if (this == o) return true;
if (!(o instanceof Book))
return false;
Book other = (Book) o;
return id != null &&
id.equals(other.getId());
}
#Override
public int hashCode() {
return getClass().hashCode();
}
In regard to HBN documentation, the problem is their synthetic cases have nothing in common with the real world. Let's consider their dummy author/book model and try to extend it... Imagine I'm a publisher and I want to keep records of my authors, their books and drafts. What is the difference between book and draft? Book has isbn assigned, draft has not, but draft may one time become a book (or may not). How to keep java equals/hashCode contracts for drafts in such case?
getClass()
In regard to the usage of getClass() everything is straightforward.
Method equals() expects an argument of type Object.
It's important to ensure that you're dialing with an instance of the same class before performing casting and comparing attributes, otherwise you can end up with a ClassCastException. And getClass() can be used for that purpose, if objects do not belong to the same class they are clearly not equal.
Natural Id vs Surrogate Id
When you're talking about "NaturalId" like ISBN-number of a book versus "id", I guess you refer to a natural key of a persistence entity versus surrogate key which is used in a relational database.
There are different opinions on that point, the general recommended approach (see a link to the Hibernate user-guide and other references below) is to use natural id (a set of unique properties, also called business keys) in your application and ID which entity obtains after being persisted only in the database.
You can encounter hashCode() and equals() that are implemented based on surrogate id, and making a defensive null-check to guard against the case when an entity is in transient state and its id is null. According to such implementations, a transient entity would not be equal to the entity in persistent state, having the same properties (apart from non-null id). Personally, I don't think this approach is correct.
The following code-sample has been taken from the most recent official Hibernate 6.1 User-Guide
Example 142. Natural Id equals/hashCode
#Entity(name = "Book")
public static class Book {
#Id
#GeneratedValue
private Long id;
private String title;
private String author;
#NaturalId
private String isbn;
//Getters and setters are omitted for brevity
#Override
public boolean equals(Object o) {
if (this == o) {
return true;
}
if (o == null || getClass() != o.getClass()) {
return false;
}
Book book = (Book) o;
return Objects.equals(isbn, book.isbn);
}
#Override
public int hashCode() {
return Objects.hash(isbn);
}
}
The code provided above that makes use of business-keys is denoted in the guide as a final approach in contrast to implementation based on the surrogate keys, which is called a naive implementation (see Example 139 and further).
The same reasoning for the choice ID vs Natural key has been described here:
You have to override the equals() and hashCode() methods if you
intend to put instances of persistent classes in a Set (the recommended way to represent many-valued associations) and
intend to use reattachment of detached instances
Hibernate guarantees equivalence of persistent identity (database row)
and Java identity only inside a particular session scope. So as soon
as we mix instances retrieved in different sessions, we must implement
equals() and hashCode() if we wish to have meaningful semantics for
Sets.
The most obvious way is to implement equals()/hashCode() by comparing
the identifier value of both objects. If the value is the same, both
must be the same database row, they are therefore equal (if both are
added to a Set, we will only have one element in the Set).
Unfortunately, we can't use that approach with generated identifiers!
Hibernate will only assign identifier values to objects that are
persistent, a newly created instance will not have any identifier
value! Furthermore, if an instance is unsaved and currently in a Set,
saving it will assign an identifier value to the object. If equals()
and hashCode() are based on the identifier value, the hash code would
change, breaking the contract of the Set. See the Hibernate website
for a full discussion of this problem. Note that this is not a
Hibernate issue, but normal Java semantics of object identity and
equality.
We recommend implementing equals() and hashCode() using Business key
equality.
For more information, have a look at this recent (Sep 15, 2021) article by #Vlad Mihalcea on how to improve caching query results with natural keys The best way to map a #NaturalId business key with JPA and Hibernate, and these questions:
The JPA hashCode() / equals() dilemma
Should the id field of a JPA entity be considered in equals and hashCode?

Is it a bad practice to return super.equals and super.hashcode in a class?

Let's say i have this class:
public class Person {
private Integer idFromDatabase;
private String name;
//Getters and setters
}
The field idFromDatabase is the attribute that should be verified in equals and used to create the hashCode. But sometimes, i am working with a list of People in memory, and have not yet stored the objects on the database, so the idFromDatabase is null for all objects, which would cause hashCode to return the same value for every object.
I solved this issue by adding the following to equals and hashCode metods:
if(idFromDatabase == null) return super.equals(o);
and
if(idFromDatabase == null) return super.hashCode();
It worked, but is it safe? Can i do it for every class that relies on a database field for equality check?
if(idFromDatabase == null) return super.equals(o); is incorrect as super's equals (if implemented correctly) does a getClass() check, which will of course be different, thus super.equals will always be false.
As already noted by #Jeroen Vannevel, if you are likely to end up with having 2 or more objects not stored in database holding the exact same information, then this technique will not help you in identifying this.
#Solver is also quite true in that a subclass is meant to have different behavior than its superclass, so you shouldn't return that they're equal.
However, in your particular example, you are just extending the Object class, so your assumption that it is safe is true (if we exclude the possibility of having 2 not-yet-persisted same Persons in memory).
Object provides the most basic equals method:
For any non-null reference values x and y, this method returns true
if and only if x and y refer to the same object
(x == y has the value true).
The hashCode method of Object:
As much as is reasonably practical, [...] does return distinct integers for distinct objects
These definitions make it clear that if you're only extending Object, then this technique is safe.
From your description I'm inferring that when comparing two People objects:
If both have an ID, they are equal if they have the same ID, even if they have different names
Otherwise, they are only equal if they are the same instance.
If that's correct, then:
#Override
public boolean equals(Object obj) {
if (this == obj)
return true;
if (this.idFromDatabase == null)
return false;
if (! (obj instanceof People))
return false;
People that = (People)obj;
if (that.idFromDatabase == null)
return false;
return this.idFromDatabase.equals(that.idFromDatabase);
}
#Override
public int hashCode() {
// Use super.hashCode to distribute objects without an idFromDatabase
return (this.idFromDatabase != null ? this.idFromDatabase.hashCode() : super.hashCode());
}
There are a few problems with your reasoning.
equals and hashcode are not subtype-friendly so it doesn't make sense to start thinking about super calls,
super is Object anyway so it's equals and hashcode are useless in this context.
What if you have two Person objects referring to the same person, but only one is stored in the database. Are they the same or different?
One universal solution is to make two classes
Person which stores a 'local' person. Doesn't contain idFromDatabase,
StoredPerson which contains idFromDatabase and a Person (or all fields of Person, but this is harder to maintain)
This way, at least equals and hashcode are well-defined and well-behaved at all times.
Implementation and usage
If you use any kind of Set/Map to store people, you now have two of them. When you save new Persons to database, you remove them from the 'local' Set/Map, wrap them in StoredPerson, and put them in the 'database' Set/Map.
If you want a searchable list of all people, make one with all Persons from both datasets into one. When you find a Person you're interested in and want to retrieve the idFromDatabase, if any, then you'd do good to prepare a map from Person to StoredPerson beforehand.
Thus you need at least,
Set<Person> localPeople = new HashSet<>();
Map<Person, StoredPerson> storedPeople = new HashMap<>();
and something like this:
void savePerson(Person person) {
synchronized (lockToPreserveInvariants) {
int id = db.insert(person);
StoredPerson sp = new StoredPerson(id, person);
localPeople.remove(person);
storedPeople.put(person, sp);
}
}

Did I override equals and hashcode correctly?

In my most recent question, I was informed that I needed to override my equals and hashcode method (among other things). So I took a bit of time to read a few articles and attempted to come up with a proper implementation.
Here are some of the articles I read:
Hashcode on Wikipedia
StackOverflow Overriding equals and hashcode
StackOverflow Why does hashcode use multiplier 31
All the articles were pretty good. Since this is my first time attempting to do this, I just want to be sure I'm not making some simple (or dumb) mistake.
I will be using name to indicate whether my Person object is equivalent to another Person object. The reason for this, is that all the other variables can vary, but the name will always be unique.
Updated to reflect recommended changes
public class Person {
private String name;
private int p_number;
private String address;
//other variables
public Person(String a_name) {
name = a_name;
}
public String getName() {
return name;
}
//other getters and setters
#Override
public boolean equals(Object o) {
if(o == null)
return false;
if(o == this)
return true;
if(!(o instanceof Person))
return false;
Person p = (Person) o;
return name.equals(p.name));
}
#Override
public int hashCode() {
return name.hashCode();
}
}
My questions are as follows:
Did I implement these methods correctly?
Since name is the only variable that determines uniqueness, do I need to bother checking any of the other variables in hashcode?
I was reading on StackOverflow that 31 was chosen as a good prime number awhile ago, but choosing a larger prime is better now? Can someone confirm or deny this claim? (the claim was made in the third link above)
If I haven't implemented these methods properly, how can I change/improve them?
In equals():
if(name.equals(p.getName()))
return true;
Missing false, and you can just:
// Both are Person instances, no need to use the accessor here
return name.equals(p.name);
As to hashCode(), just return name.hashCode();
Also, can name be null? Your methods don't seem to account for that. (edit: answer: no)
As to your questions:
Since name is the only variable that determines uniqueness, do I need to bother checking any of the other variables in hashcode?
NO, certainly not! If your names are equal but ages different, this would lead to a different hash code for objects which are equal, and this violates the Object contract!
I was reading on StackOverflow that 31 was chosen as a good prime number awhile ago, but choosing a larger prime is better now? Can someone confirm or deny this claim? (the claim was made in the third link above)
This, no idea...
To be more complete about the .equals()/.hashCode() contract, I'll mention a utility class from Guava: Equivalence. An implementation of this abstract class for a given
class can allow you to create Sets, and therefore Maps, with these objects as members (keys) as if they had a different implementation of both of these functions:
Equivalence<MyClass> eq = ....;
Set<Equivalence.Wrapper<MyClass>> set = ...;
set.add(eq.wrap(myClassInstance));
This can be actually very useful in some scenarios...
Your equals needs to return a value in all cases, just change the end part to return the name.equals.
#Override
public boolean equals(Object o) {
...
return name.equals(o.getName());
}
Also your hashcode is actually detrimental, all it does is use the name.hashCode() but then multiply it by 31, better to use the default Java string hashcode from the name directly.
#Override
public int hashCode() {
return name.hashCode();
}

Why are these two instances not `equal()`? [duplicate]

This question already has answers here:
Closed 10 years ago.
Possible Duplicate:
Why .equals method is failing on two same value objects?
This is really simple but I'm obviously missing something pretty big here.
Cat cat1 = new Cat("bob");
Cat cat2 = new Cat("bob");
System.out.println(cat1 == cat2);
//false since these references point to diferent objects
System.out.println(cat1.equals(cat2));
//returns false also??
Cat is just a simple class that only has a name.
What is going on here, how does equals() work? I was under the impression that it compared all the fields of the object. It seems that is not the case.
Do I need to overide it for all my classes?
Yes.
java.lang.Object provides very basic implementations of equals() and hashCode(). In particular, they don't go around reflecting on the type of the instance, which would (1) be dreadfully slow, and (2) carry a significant risk of comparing fields that you for various reasons don't want to compare in an equality comparison.
If you want equals() and hashCode() to actually be useful for comparing value equality (rather than reference equality which == does), you'll need to implement both within your own type.
Note that it's not enough to implement just equals(); while technically that will "work", it has the potential to lead to all kinds of weirdness. The simple rule of thumb is: neither or both, but never only one. And they must work on the same fields; if equals() says two instances are equal, then calling hashCode() on both must return the same value (also see the hashCode() contract).
It's also usually a good idea to override toString() with code to provide a meaningful description of the object in question. While not strictly needed, you only need to hit your head against this once in the debugger to realize the value. (Thanks #JonTaylor for mentioning this highly useful, related tidbit.)
And it's .NET that calls it GetHashCode(), while Java uses only hashCode() as the function name...
You need to override equals inside your Cat class. Default equals compares objects on references.
class Cat {
private String name;
public Cat(String name) {
this.name = name;
}
#Override
public boolean equals(Object obj) {
if (obj == null)
return false;
if (!(obj instanceof Cat))
return false;
Cat c = (Cat) obj;
return this.name == null ? false : this.name.equals(c.name);
}
#Override
public int hashCode() {
return this.name == null ? 31 : this.name.hashCode();
}
#Override
public String toString() {
return "Cat Name :" + name;
}
}
References
equals
hashCode
toString
The equals() provided by java.lang.object compares, simply speaking, a unique identifier for the object, though not entirely accurate you can think of it as a memory location, so it will only be true if you compare an object with itself (i.e. two references to the same object in memory)
You need to implement your own equals() method in your Cat class:
class Cat
{
String name;
#Override
public boolean equals(Cat other)
{
if (this.name.equals(other.name))
return true;
return false;
}
}
It would be wise to override hashCode() also, unless this is just a very basic application for homework or something. Also toString() can be useful to override as well.
http://docs.oracle.com/javase/tutorial/java/IandI/objectclass.html
From [Java Doc]
The equals method for class Object implements the most discriminating
possible equivalence relation on objects; that is, for any non-null
reference values x and y, this method returns true if and only if x
and y refer to the same object (x == y has the value true).
Without overriding the equals() method, the objects are different
Hence
System.out.println(cat1.equals(cat2)); // is false
That is because the == compare references and java.lang.Object.equals() translates to this==o thus return same as == in your case
In the case above you are using new operator to create two different objects hence both return false.
If you want .equals() to work as you are expecting, then override theequals() in your Cat class.

Can I use an Entity's ID in equals/hashCode with fallback to instance equality?

I'm trying to figure out what's wrong with this approach, given my particular usage patterns:
#Entity
public class DomainObject {
#Id // + sequence generator
private Long id;
#Override
public boolean equals(Object o) {
// bunch of other checks omitted for clarity
if (id != null) {
return id.equals(o.getId());
}
return super.equals(o);
}
#Override
public int hashCode() {
if (id != null) {
return id.hashCode();
}
return super.hashCode();
}
I've read several posts on the subject and it sounds like you don't want to use a DB-generated sequence value in equals/hashCode because they won't be set until the objects are persisted and you don't want disparate transient instances to all be equal, or the persistence layer itself might break.
But is there anything wrong with falling back to the default Object equals/hashCode (instance equality) for transient objects and then using the generated #Id when you have it?
The worst thing I can think of is, a transient object can't ever be equal to a persistent object, which is fine in my use case - the only time I'm ever putting objects in collections and want contains to work, all the objects are already persistent and all have IDs.
However, I feel like there's something else wrong in a really subtle, non-obvious way deep in the persistence layer but I can't quite figure out what.
The other options don't seem that appealing either:
doing nothing and living with instance equality (default Object.equals): works great for most of my entities, but getting tired of workarounds for the handful of cases when I want a collection with a mix of detached entities (e.g., session scope) and "live" ones from current transaction
using a business key: I have clear natural keys but they're mutable, and this would have
some of the same problems as above (hashCode stability if object changes)
using a UUID - I know that will work, but feels wrong to pollute the DB with artifacts to support java.util collections.
See also:
The JPA hashCode() / equals() dilemma
Should the id field of a JPA entity be considered in equals and hashCode?.
The javadoc of Map writes:
Note: great care must be exercised if mutable objects are used as map keys. The behavior of a map is not specified if the value of an object is changed in a manner that affects equals comparisons while the object is a key in the map.
Whenever an object is persisted, your implementation changes the meaning of equals. As such, any Collections containing that object need no longer work right. In particular, changing the hashcode of an object used as key in a HashMap (or contained in HashSet) is likely to result in future lookups on that Map (Set) not finding the object, and adding that object again to the Map (Set) is likely to succeed, even though under ordinary circumstances, a Map may contain at most one mapping for every given key, and a Set contain every object at most once.
Since it is common to store entities in collections (to express ToMany-associations), that flaw is likely to result in actual hard-to-find bugs.
I therefore strongly recommend against implementing hashcode based on database-generated identifiers.
If you're sure you don't ever need to add an unpersisted entity to a Set or Map key, you can use the ID to test for equality and as the hash code. But if you do this, you could enforce it by throwing an Exception for an unpersisted object:
#Entity
public class DomainObject {
#Id // + sequence generator
private Long id;
#Override
public boolean equals(Object that) {
// bunch of other checks omitted for clarity
if (id != null) {
throw new IllegalStateException("equals() before persisting");
}
if (this == that) {
return true;
}
if (that instanceof DomainObject) {
return id.equals(((DomainObject)that).id);
}
}
#Override
public int hashCode() {
if (id != null) {
throw new IllegalStateException("hashCode() before persisting");
}
return id;
}
}
If you do this, you may see surprise exceptions, where you didn't realize you were relying on these methods on unpersisted objects. You may find this helpful in debugging. You might also find it makes your existing code unusable. Either way, you'll be clearer about how your code works.
One thing you should never do is return a constant for the hash code.
public int hashCode() { return 5; } // Don't ever do this!
Technically, it fulfills the contract, but it's a terrible implementation. Just read the javadocs for Object.hashCode(): …producing distinct integer results for unequal objects may improve the performance of hash tables. (The word "may" here is a serious understatement.)
Yes, you can! But you have to be careful that the hashCode implementation always returns the same constant value as explained in this post:
#Entity
public class Book implements Identifiable<Long> {
#Id
#GeneratedValue
private Long id;
private String title;
#Override
public boolean equals(Object o) {
if (this == o) return true;
if (!(o instanceof Book)) return false;
Book book = (Book) o;
return Objects.equals(getId(), book.getId());
}
#Override
public int hashCode() {
return getClass().hashCode();
}
//Getters and setters omitted for brevity
}
This is the only way you can ensure that equals and hashCode are consistent across all entity state transitions.

Categories