In many situations working with Java object, we need define toString(), equals() or hashCode() method. These methods can be added to the Java object automatically by using Project Lombok. In this tutorial, I will show you all about this!
First of all, we need a Maven project for example:
with Project Lombok dependency as below:
1 2 3 4 5 6 |
<dependency> <groupId>org.projectlombok</groupId> <artifactId>lombok</artifactId> <version>1.18.24</version> <scope>provided</scope> </dependency> |
Remember that, we need install Project Lombok plugin into our IDE. See construction for IntelliJ IDEA at here.
Now, for example, I have a Student class with some information like name, code, dateOfBirth. Without using Lombok library, I must declare the method toString(), equals() and hashCode() method (other Getter, Setter methods or constructors, I defined with Project Lombok) like below:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 |
package com.huongdanjava.lombok; import lombok.*; import java.util.Date; @Getter @Setter @NoArgsConstructor @AllArgsConstructor public class Student { private String name; private transient String code; private Date dateOfBirth; public Student(String name) { this.name = name; } @Override public boolean equals(Object o) { if (this == o) return true; if (o == null || getClass() != o.getClass()) return false; Student student = (Student) o; if (!name.equals(student.name)) return false; if (!code.equals(student.code)) return false; return dateOfBirth.equals(student.dateOfBirth); } @Override public int hashCode() { int result = name.hashCode(); result = 31 * result + code.hashCode(); result = 31 * result + dateOfBirth.hashCode(); return result; } @Override public String toString() { return "Student{" + "name='" + name + '\'' + ", code='" + code + '\'' + ", dateOfBirth=" + dateOfBirth + '}'; } } |
We can automate this by using @ToString and @EqualsAndHashCode annotation as below:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 |
package com.huongdanjava.lombok; import lombok.*; import java.util.Date; @Getter @Setter @NoArgsConstructor @AllArgsConstructor @ToString @EqualsAndHashCode public class Student { private String name; private transient String code; private Date dateOfBirth; public Student(String name) { this.name = name; } } |
- @ToString: will generate toString() method with all class fields. We no need to maintain this method when we add more and remove some fields.
- @EqualsAndHashCode: this annotation will help Project Lombok generate the method equals() and hashCode() with all class nonstatic and nontransient fields automatically.
Now, if you check in the folder /target/classes/com/huongdanjava/lombok, the content of Student.class will be as below:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 |
// // Source code recreated from a .class file by IntelliJ IDEA // (powered by Fernflower decompiler) // package com.huongdanjava.lombok; import java.beans.ConstructorProperties; import java.util.Date; public class Student { private String name; private transient String code; private Date dateOfBirth; public Student(String name) { this.name = name; } public String getName() { return this.name; } public String getCode() { return this.code; } public Date getDateOfBirth() { return this.dateOfBirth; } public void setName(String name) { this.name = name; } public void setCode(String code) { this.code = code; } public void setDateOfBirth(Date dateOfBirth) { this.dateOfBirth = dateOfBirth; } public Student() { } @ConstructorProperties({"name", "code", "dateOfBirth"}) public Student(String name, String code, Date dateOfBirth) { this.name = name; this.code = code; this.dateOfBirth = dateOfBirth; } public String toString() { return "Student(name=" + this.getName() + ", code=" + this.getCode() + ", dateOfBirth=" + this.getDateOfBirth() + ")"; } public boolean equals(Object o) { if(o == this) { return true; } else if(!(o instanceof Student)) { return false; } else { Student other = (Student)o; if(!other.canEqual(this)) { return false; } else { Object this$name = this.getName(); Object other$name = other.getName(); if(this$name == null) { if(other$name != null) { return false; } } else if(!this$name.equals(other$name)) { return false; } Object this$dateOfBirth = this.getDateOfBirth(); Object other$dateOfBirth = other.getDateOfBirth(); if(this$dateOfBirth == null) { if(other$dateOfBirth != null) { return false; } } else if(!this$dateOfBirth.equals(other$dateOfBirth)) { return false; } return true; } } } protected boolean canEqual(Object other) { return other instanceof Student; } public int hashCode() { int PRIME = true; int result = 1; Object $name = this.getName(); int result = result * 59 + ($name == null?43:$name.hashCode()); Object $dateOfBirth = this.getDateOfBirth(); result = result * 59 + ($dateOfBirth == null?43:$dateOfBirth.hashCode()); return result; } } |
You can see, because the field code in Student bean was declared as the transient field, then in generated equals() and hashCode() method, we didn’t see it included.
Now, if we have 2 Student objects with different codes, same name and dateOfBirth, when comparing, you will still see the same. For example:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 |
package com.huongdanjava.lombok; public class Example { public static void main(String[] args) { Student student1 = new Student(); student1.setName("Khanh"); student1.setCode("abc"); Student student2 = new Student(); student2.setName("Khanh"); student2.setCode("bcd"); System.out.println(student1.equals(student2)); } } |
Result:
You can exclude some fields which we don’t want to include in toString(), equals() and hashCode() method by declaring exclude attribute in @ToString and @EqualsAndHashCode annotation.
For example, now, I want to exclude dateOfBirth field in Student bean, I will declare as below:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 |
package com.huongdanjava.lombok; import lombok.*; import java.util.Date; @Getter @Setter @NoArgsConstructor @AllArgsConstructor @ToString(exclude = "dateOfBirth") @EqualsAndHashCode(exclude = "dateOfBirth") public class Student { private String name; private transient String code; private Date dateOfBirth; public Student(String name) { this.name = name; } } |
Now, if you check in the folder /target/classes/com/huongdanjava/lombok, the content of Student.class will be as below:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 |
// // Source code recreated from a .class file by IntelliJ IDEA // (powered by Fernflower decompiler) // package com.huongdanjava.lombok; import java.beans.ConstructorProperties; import java.util.Date; public class Student { private String name; private transient String code; private Date dateOfBirth; public Student(String name) { this.name = name; } public String getName() { return this.name; } public String getCode() { return this.code; } public Date getDateOfBirth() { return this.dateOfBirth; } public void setName(String name) { this.name = name; } public void setCode(String code) { this.code = code; } public void setDateOfBirth(Date dateOfBirth) { this.dateOfBirth = dateOfBirth; } public Student() { } @ConstructorProperties({"name", "code", "dateOfBirth"}) public Student(String name, String code, Date dateOfBirth) { this.name = name; this.code = code; this.dateOfBirth = dateOfBirth; } public String toString() { return "Student(name=" + this.getName() + ", code=" + this.getCode() + ")"; } public boolean equals(Object o) { if(o == this) { return true; } else if(!(o instanceof Student)) { return false; } else { Student other = (Student)o; if(!other.canEqual(this)) { return false; } else { Object this$name = this.getName(); Object other$name = other.getName(); if(this$name == null) { if(other$name != null) { return false; } } else if(!this$name.equals(other$name)) { return false; } return true; } } } protected boolean canEqual(Object other) { return other instanceof Student; } public int hashCode() { int PRIME = true; int result = 1; Object $name = this.getName(); int result = result * 59 + ($name == null?43:$name.hashCode()); return result; } } |
DoomGuy
Thanks for the clarification – @EqualsAndHashCode(exclude = “dateOfBirth”)