diff --git a/core-java-lang/pom.xml b/core-java-lang/pom.xml
index ace39de274..2f307859f1 100644
--- a/core-java-lang/pom.xml
+++ b/core-java-lang/pom.xml
@@ -66,6 +66,12 @@
mail
${javax.mail.version}
+
+ nl.jqno.equalsverifier
+ equalsverifier
+ ${equalsverifier.version}
+ test
+
@@ -424,6 +430,7 @@
3.1.1
2.0.3.RELEASE
1.6.0
+ 3.0.3
diff --git a/core-java-lang/src/main/java/com/baeldung/equalshashcode/Money.java b/core-java-lang/src/main/java/com/baeldung/equalshashcode/Money.java
new file mode 100644
index 0000000000..60c043545d
--- /dev/null
+++ b/core-java-lang/src/main/java/com/baeldung/equalshashcode/Money.java
@@ -0,0 +1,36 @@
+package com.baeldung.equalshashcode;
+
+class Money {
+
+ int amount;
+ String currencyCode;
+
+ Money(int amount, String currencyCode) {
+ this.amount = amount;
+ this.currencyCode = currencyCode;
+ }
+
+ @Override
+ public boolean equals(Object o) {
+ if (o == this)
+ return true;
+ if (!(o instanceof Money))
+ return false;
+ Money other = (Money)o;
+ boolean currencyCodeEquals = (this.currencyCode == null && other.currencyCode == null)
+ || (this.currencyCode != null && this.currencyCode.equals(other.currencyCode));
+ return this.amount == other.amount
+ && currencyCodeEquals;
+ }
+
+ @Override
+ public int hashCode() {
+ int result = 17;
+ result = 31 * result + amount;
+ if (currencyCode != null) {
+ result = 31 * result + currencyCode.hashCode();
+ }
+ return result;
+ }
+
+}
diff --git a/core-java-lang/src/main/java/com/baeldung/equalshashcode/Team.java b/core-java-lang/src/main/java/com/baeldung/equalshashcode/Team.java
new file mode 100644
index 0000000000..c2dee1de6b
--- /dev/null
+++ b/core-java-lang/src/main/java/com/baeldung/equalshashcode/Team.java
@@ -0,0 +1,39 @@
+package com.baeldung.equalshashcode;
+
+class Team {
+
+ final String city;
+ final String department;
+
+ Team(String city, String department) {
+ this.city = city;
+ this.department = department;
+ }
+
+ @Override
+ public final boolean equals(Object o) {
+ if (o == this)
+ return true;
+ if (!(o instanceof Team))
+ return false;
+ Team otherTeam = (Team)o;
+ boolean cityEquals = (this.city == null && otherTeam.city == null)
+ || this.city != null && this.city.equals(otherTeam.city);
+ boolean departmentEquals = (this.department == null && otherTeam.department == null)
+ || this.department != null && this.department.equals(otherTeam.department);
+ return cityEquals && departmentEquals;
+ }
+
+ @Override
+ public final int hashCode() {
+ int result = 17;
+ if (city != null) {
+ result = 31 * result + city.hashCode();
+ }
+ if (department != null) {
+ result = 31 * result + department.hashCode();
+ }
+ return result;
+ }
+
+}
diff --git a/core-java-lang/src/main/java/com/baeldung/equalshashcode/Voucher.java b/core-java-lang/src/main/java/com/baeldung/equalshashcode/Voucher.java
new file mode 100644
index 0000000000..19f46e0358
--- /dev/null
+++ b/core-java-lang/src/main/java/com/baeldung/equalshashcode/Voucher.java
@@ -0,0 +1,38 @@
+package com.baeldung.equalshashcode;
+
+class Voucher {
+
+ private Money value;
+ private String store;
+
+ Voucher(int amount, String currencyCode, String store) {
+ this.value = new Money(amount, currencyCode);
+ this.store = store;
+ }
+
+ @Override
+ public boolean equals(Object o) {
+ if (o == this)
+ return true;
+ if (!(o instanceof Voucher))
+ return false;
+ Voucher other = (Voucher)o;
+ boolean valueEquals = (this.value == null && other.value == null)
+ || (this.value != null && this.value.equals(other.value));
+ boolean storeEquals = (this.store == null && other.store == null)
+ || (this.store != null && this.store.equals(other.store));
+ return valueEquals && storeEquals;
+ }
+
+ @Override
+ public int hashCode() {
+ int result = 17;
+ if (this.value != null) {
+ result = 31 * result + value.hashCode();
+ }
+ if (this.store != null) {
+ result = 31 * result + store.hashCode();
+ }
+ return result;
+ }
+}
\ No newline at end of file
diff --git a/core-java-lang/src/main/java/com/baeldung/equalshashcode/WrongTeam.java b/core-java-lang/src/main/java/com/baeldung/equalshashcode/WrongTeam.java
new file mode 100644
index 0000000000..c4477aa790
--- /dev/null
+++ b/core-java-lang/src/main/java/com/baeldung/equalshashcode/WrongTeam.java
@@ -0,0 +1,30 @@
+package com.baeldung.equalshashcode;
+
+/* (non-Javadoc)
+* This class overrides equals, but it doesn't override hashCode.
+*
+* To see which problems this leads to:
+* TeamUnitTest.givenMapKeyWithoutHashCode_whenSearched_thenReturnsWrongValue
+*/
+class WrongTeam {
+
+ String city;
+ String department;
+
+ WrongTeam(String city, String department) {
+ this.city = city;
+ this.department = department;
+ }
+
+ @Override
+ public boolean equals(Object o) {
+ if (o == this)
+ return true;
+ if (!(o instanceof WrongTeam))
+ return false;
+ WrongTeam otherTeam = (WrongTeam)o;
+ return this.city == otherTeam.city
+ && this.department == otherTeam.department;
+ }
+
+}
diff --git a/core-java-lang/src/main/java/com/baeldung/equalshashcode/WrongVoucher.java b/core-java-lang/src/main/java/com/baeldung/equalshashcode/WrongVoucher.java
new file mode 100644
index 0000000000..97935bf8de
--- /dev/null
+++ b/core-java-lang/src/main/java/com/baeldung/equalshashcode/WrongVoucher.java
@@ -0,0 +1,47 @@
+package com.baeldung.equalshashcode;
+
+/* (non-Javadoc)
+* This class extends the Money class that has overridden the equals method and once again overrides the equals method.
+*
+* To see which problems this leads to:
+* MoneyUnitTest.givenMoneyAndVoucherInstances_whenEquals_thenReturnValuesArentSymmetric
+*/
+class WrongVoucher extends Money {
+
+ private String store;
+
+ WrongVoucher(int amount, String currencyCode, String store) {
+ super(amount, currencyCode);
+
+ this.store = store;
+ }
+
+ @Override
+ public boolean equals(Object o) {
+ if (o == this)
+ return true;
+ if (!(o instanceof WrongVoucher))
+ return false;
+ WrongVoucher other = (WrongVoucher)o;
+ boolean currencyCodeEquals = (this.currencyCode == null && other.currencyCode == null)
+ || (this.currencyCode != null && this.currencyCode.equals(other.currencyCode));
+ boolean storeEquals = (this.store == null && other.store == null)
+ || (this.store != null && this.store.equals(other.store));
+ return this.amount == other.amount
+ && currencyCodeEquals
+ && storeEquals;
+ }
+
+ @Override
+ public int hashCode() {
+ int result = 17;
+ result = 31 * result + amount;
+ if (this.currencyCode != null) {
+ result = 31 * result + currencyCode.hashCode();
+ }
+ if (this.store != null) {
+ result = 31 * result + store.hashCode();
+ }
+ return result;
+ }
+}
\ No newline at end of file
diff --git a/core-java-lang/src/test/java/com/baeldung/equalshashcode/MoneyUnitTest.java b/core-java-lang/src/test/java/com/baeldung/equalshashcode/MoneyUnitTest.java
new file mode 100644
index 0000000000..60584fdb53
--- /dev/null
+++ b/core-java-lang/src/test/java/com/baeldung/equalshashcode/MoneyUnitTest.java
@@ -0,0 +1,27 @@
+package com.baeldung.equalshashcode;
+
+import static org.junit.Assert.assertTrue;
+import static org.junit.Assert.assertFalse;
+
+import org.junit.Test;
+
+public class MoneyUnitTest {
+
+ @Test
+ public void givenMoneyInstancesWithSameAmountAndCurrency_whenEquals_thenReturnsTrue() {
+ Money income = new Money(55, "USD");
+ Money expenses = new Money(55, "USD");
+
+ assertTrue(income.equals(expenses));
+ }
+
+ @Test
+ public void givenMoneyAndVoucherInstances_whenEquals_thenReturnValuesArentSymmetric() {
+ Money cash = new Money(42, "USD");
+ WrongVoucher voucher = new WrongVoucher(42, "USD", "Amazon");
+
+ assertFalse(voucher.equals(cash));
+ assertTrue(cash.equals(voucher));
+ }
+
+}
diff --git a/core-java-lang/src/test/java/com/baeldung/equalshashcode/TeamUnitTest.java b/core-java-lang/src/test/java/com/baeldung/equalshashcode/TeamUnitTest.java
new file mode 100644
index 0000000000..a2de408796
--- /dev/null
+++ b/core-java-lang/src/test/java/com/baeldung/equalshashcode/TeamUnitTest.java
@@ -0,0 +1,46 @@
+package com.baeldung.equalshashcode;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
+
+import java.util.HashMap;
+import java.util.Map;
+
+import org.junit.Test;
+
+import nl.jqno.equalsverifier.EqualsVerifier;
+
+public class TeamUnitTest {
+
+ @Test
+ public void givenMapKeyWithHashCode_whenSearched_thenReturnsCorrectValue() {
+ Map leaders = new HashMap<>();
+ leaders.put(new Team("New York", "development"), "Anne");
+ leaders.put(new Team("Boston", "development"), "Brian");
+ leaders.put(new Team("Boston", "marketing"), "Charlie");
+
+ Team myTeam = new Team("New York", "development");
+ String myTeamleader = leaders.get(myTeam);
+
+ assertEquals("Anne", myTeamleader);
+ }
+
+ @Test
+ public void givenMapKeyWithoutHashCode_whenSearched_thenReturnsWrongValue() {
+ Map leaders = new HashMap<>();
+ leaders.put(new WrongTeam("New York", "development"), "Anne");
+ leaders.put(new WrongTeam("Boston", "development"), "Brian");
+ leaders.put(new WrongTeam("Boston", "marketing"), "Charlie");
+
+ WrongTeam myTeam = new WrongTeam("New York", "development");
+ String myTeamleader = leaders.get(myTeam);
+
+ assertFalse("Anne".equals(myTeamleader));
+ }
+
+ @Test
+ public void equalsContract() {
+ EqualsVerifier.forClass(Team.class).verify();
+ }
+
+}