I have a class having more than 30 attributes.
I want to override the equals method in order to compare two instance of my class.
However I want to avoid re-write all the 30 attributes in my method as this
#Override
public boolean equals(java.lang.Object o) {
if (this == o) {
return true;
}
if (o == null || getClass() != o.getClass()) {
return false;
}
Address address = (Address) o;
return Objects.equals(this.attr1, address.attr1) &&
Objects.equals(this.attr2, address.attr2) &&
Objects.equals(this.attr3, address.attr3) &&
......
Objects.equals(this.attr30, address.attr30);
}
Have you a more simple and proper way ?
Well, this is basically the type of boilerplate code that is necessary. Luckily, there are lots of developers just as annoyed of writing such code as you are. For reasons like that, Project Lombok was founded.
Please see this link.
As an example, see the following two code snippets extracted from the page that I linked above:
Lombok
import lombok.EqualsAndHashCode;
#EqualsAndHashCode(exclude={"id", "shape"})
public class EqualsAndHashCodeExample {
private transient int transientVar = 10;
private String name;
private double score;
private Shape shape = new Square(5, 10);
private String[] tags;
private int id;
public String getName() {
return this.name;
}
#EqualsAndHashCode(callSuper=true)
public static class Square extends Shape {
private final int width, height;
public Square(int width, int height) {
this.width = width;
this.height = height;
}
}
}
Vanilla Java
import java.util.Arrays;
public class EqualsAndHashCodeExample {
private transient int transientVar = 10;
private String name;
private double score;
private Shape shape = new Square(5, 10);
private String[] tags;
private int id;
public String getName() {
return this.name;
}
#Override public boolean equals(Object o) {
if (o == this) return true;
if (!(o instanceof EqualsAndHashCodeExample)) return false;
EqualsAndHashCodeExample other = (EqualsAndHashCodeExample) o;
if (!other.canEqual((Object)this)) return false;
if (this.getName() == null ? other.getName() != null : !this.getName().equals(other.getName())) return false;
if (Double.compare(this.score, other.score) != 0) return false;
if (!Arrays.deepEquals(this.tags, other.tags)) return false;
return true;
}
#Override public int hashCode() {
final int PRIME = 59;
int result = 1;
final long temp1 = Double.doubleToLongBits(this.score);
result = (result*PRIME) + (this.name == null ? 43 : this.name.hashCode());
result = (result*PRIME) + (int)(temp1 ^ (temp1 >>> 32));
result = (result*PRIME) + Arrays.deepHashCode(this.tags);
return result;
}
protected boolean canEqual(Object other) {
return other instanceof EqualsAndHashCodeExample;
}
public static class Square extends Shape {
private final int width, height;
public Square(int width, int height) {
this.width = width;
this.height = height;
}
#Override public boolean equals(Object o) {
if (o == this) return true;
if (!(o instanceof Square)) return false;
Square other = (Square) o;
if (!other.canEqual((Object)this)) return false;
if (!super.equals(o)) return false;
if (this.width != other.width) return false;
if (this.height != other.height) return false;
return true;
}
#Override public int hashCode() {
final int PRIME = 59;
int result = 1;
result = (result*PRIME) + super.hashCode();
result = (result*PRIME) + this.width;
result = (result*PRIME) + this.height;
return result;
}
protected boolean canEqual(Object other) {
return other instanceof Square;
}
}
}
If you like this approach, I'd recommend checking out Project Lombok in its entirety. It really helps cleaning up your code!
!!! Beware !!!
In order to be able to actually use methods generated by Lombok, you need to install the Lombok plugin into your IDE! Otherwise, your IDE won't know of the automatically generated methods.
You can use the Field class in the java.lang.reflect package like so:
#Override
public boolean equals(Object o) {
//instanceof check, null check, etc
Field[] fields = Address.class.getDeclaredFields();
for (Field field : fields) {
try {
field.setAccessible(true);
if (!field.get(this).equals(field.get((Address) o))) {
return false;
} //end if
} catch (IllegalAccessException e) {
//handle exception
} //end try catch
} //end for
return true;
} //equals
You have this method in apache commons library that uses reflection for compare them
org.apache.commons.lang.builder.EqualsBuilder.reflectionEquals(Object, Object)
Example
import org.apache.commons.lang.builder.EqualsBuilder;
public class MyObject {
...
#Override
public boolean equals(Object obj) {
return EqualsBuilder.reflectionEquals(this, obj);
}
}
You can use lombok project to auto-generate hashCode and equals methods at build time .
You can use Unitils http://www.unitils.org/cookbook.html
import static org.unitils.reflectionassert.ReflectionAssert.*;
// Exact field-by-field comparison
assertReflectionEquals(new Person("John", "Doe", new Address("New street", 5, "Brussels")),
new Person("John", "Doe", new Address("New street", 5, "Brussels"));
Related
I'm writing a program which contains an abstract class of 'Book', and I have two classes ('LearnBook' and 'ReadingBook') which inherit from 'Book'.
Book:
Public abstract class Book {
protected String name;
protected String author;
LearningBook:
public class LearningBook extends Book {
private String subject;
ReadingBook:
public class ReadingBook extends Book {
private int numberOfPages;
At the main class I have Book array which can include any instance of Book.
I want to add a method which checks if two Book objects are exactly the same, to prevent duplicating in the Book array. it looks like this:
public boolean sameBookCheck(Book book1, Book book2)
So my first idea was to write an isEqual() method in the Book class, which checks if the "name" and the "author" are equals.
But then I need to check if it's a learning book or reading book so I could know if I need to compare the "subject" value or the "numberOfPage" value.
I have no idea how to do it and I'd appreciate your help.
You can use the following design:
In Book abstract class have an equals() function and check whether the other object is of type Book and have same values in all fields.
In LearningBook and ReadingBook have equals() function which first checks whether the other object is of same class, then call Book's equals() function, checking the fields of abstract class, and then check whether field(s) of current class have same values or not.
Have a look at the code:
abstract class Book {
protected String name;
protected String author;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getAuthor() {
return author;
}
public void setAuthor(String author) {
this.author = author;
}
#Override
public int hashCode() {
final int prime = 31;
int result = 1;
result = prime * result + ((author == null) ? 0 : author.hashCode());
result = prime * result + ((name == null) ? 0 : name.hashCode());
return result;
}
#Override
public boolean equals(Object obj) {
if (this == obj)
return true;
if (obj == null)
return false;
if (!(obj instanceof Book))
return false;
Book other = (Book) obj;
if (author == null) {
if (other.author != null)
return false;
} else if (!author.equals(other.author))
return false;
if (name == null) {
if (other.name != null)
return false;
} else if (!name.equals(other.name))
return false;
return true;
}
}
class LearningBook extends Book{
private String subject;
public String getSubject() {
return subject;
}
public void setSubject(String subject) {
this.subject = subject;
}
#Override
public int hashCode() {
final int prime = 31;
int result = super.hashCode();
result = prime * result + ((subject == null) ? 0 : subject.hashCode());
return result;
}
#Override
public boolean equals(Object obj) {
if (this == obj)
return true;
if (!super.equals(obj))
return false;
if (!(obj instanceof LearningBook))
return false;
LearningBook other = (LearningBook) obj;
if (subject == null) {
if (other.subject != null)
return false;
} else if (!subject.equals(other.subject))
return false;
return true;
}
}
class ReadingBook extends Book{
private int numberOfPages;
public int getNumberOfPages() {
return numberOfPages;
}
public void setNumberOfPages(int numberOfPages) {
this.numberOfPages = numberOfPages;
}
#Override
public int hashCode() {
final int prime = 31;
int result = super.hashCode();
result = prime * result + numberOfPages;
return result;
}
#Override
public boolean equals(Object obj) {
if (this == obj)
return true;
if (!super.equals(obj))
return false;
if (!(obj instanceof ReadingBook))
return false;
ReadingBook other = (ReadingBook) obj;
if (numberOfPages != other.numberOfPages)
return false;
return true;
}
}
public class Runner {
public static void main(String[] args) {
Book learningBook = new LearningBook();
learningBook.setAuthor("auth");
learningBook.setName("sci");
Book learningBook2 = new LearningBook();
learningBook2.setAuthor("auth");
learningBook2.setName("sci");
Book readingBook = new ReadingBook();
readingBook.setAuthor("auth");
readingBook.setName("sci");
//returns false
System.out.println(learningBook.equals(readingBook) );
//returns true
System.out.println(learningBook.equals(learningBook2) );
}
}
Write an equals-implementation for each of the three classes. Every implementation is only responsible for its own fields.
The equals-implementations from the sub-classes ReadingBook and LearningBook should somewhere call super.equals() - the equals-implementation of Book.
You can ask the book instance for its class and check class equality.
book1.getClass().equals(book2.getClass())
You can use instanceof method to compare the type of the Object. To check if it is a type of LearningBook or ReadingBook example
Answer for your comment,
Lets say when you check the two instance it says they are different, then there is no issue you can return false. But if the instances are also same then you can check it with something like this after that
if (both instances are same) {
if (yourObjectIs instanceof LearningBook) {
you can check the two values of LearningBook here and return true if the are equals
} else {
you can check the two values of ReadingBook here and return true if the are equals
}
}
As it was mentioned you should overwrite equals(Object object) method. In your example you can do it like this:
public abstract class Book{
#NonNull protected String name;
#NonNull protected String author;
public Book(String name, String author) {
this.name = name;
this.author = author;
}
#Override
public boolean equals(Object object) {
if (object instanceof Book) {
var book = (Book) object;
return this.name.equals(book.name) && this.author.equals(book.author);
} else
return false;
}
}
public class LearningBook extends Book{
#NonNull private String subject;
public LearningBook(String name, String author,String subject) {
super(name, author);
this.subject = subject;
}
#Override
public boolean equals(Object object) {
if (object instanceof LearningBook) {
var book = (LearningBook) object;
return this.subject.equals(book.subject) && super.equals(book);
} else
return false;
}
}
public class ReadingBook extends Book{
#NonNull private int numberOfPages;
public ReadingBook(String name, String author,int numberOfPages) {
super(name, author);
this.numberOfPages = numberOfPages;
}
#Override
public boolean equals(Object object) {
if (object instanceof ReadingBook) {
var book = (ReadingBook) object;
return super.equals(book) && this.numberOfPages == book.numberOfPages;
} else
return false;
}
}
I've used #NonNull annotation to avoid NPE in equals method.
I'm currently in need of some guidance. Instead of making a huge constructor class with gets/sets. Is it possible to simplify this task?
Trying to avoid having a huge constructor with gets/sets. So I'm assuming what's a good way to avoid doing something like this. How can this sort of thing be simplified significantly?
public User(int id, String name, long skillPoints) {
this.id = id;
this.name = name;
this.skillPoints = skillPoints;
this.level = 0;
// So on so forth
}
Have you heard of Project Lombok?
By adding the annotation #Data you will get a shortcut for #ToString, #EqualsAndHashCode, #Getter on all fields, #Setter on all non-final fields, and #RequiredArgsConstructor. And there are plenty more annotations you can check out!
With Lombok
import lombok.AccessLevel;
import lombok.Setter;
import lombok.Data;
import lombok.ToString;
#Data public class DataExample {
private final String name;
#Setter(AccessLevel.PACKAGE) private int age;
private double score;
private String[] tags;
#ToString(includeFieldNames=true)
#Data(staticConstructor="of")
public static class Exercise<T> {
private final String name;
private final T value;
}
}
Vanilla Java
import java.util.Arrays;
public class DataExample {
private final String name;
private int age;
private double score;
private String[] tags;
public DataExample(String name) {
this.name = name;
}
public String getName() {
return this.name;
}
void setAge(int age) {
this.age = age;
}
public int getAge() {
return this.age;
}
public void setScore(double score) {
this.score = score;
}
public double getScore() {
return this.score;
}
public String[] getTags() {
return this.tags;
}
public void setTags(String[] tags) {
this.tags = tags;
}
#Override public String toString() {
return "DataExample(" + this.getName() + ", " + this.getAge() + ", " + this.getScore() + ", " + Arrays.deepToString(this.getTags()) + ")";
}
protected boolean canEqual(Object other) {
return other instanceof DataExample;
}
#Override public boolean equals(Object o) {
if (o == this) return true;
if (!(o instanceof DataExample)) return false;
DataExample other = (DataExample) o;
if (!other.canEqual((Object)this)) return false;
if (this.getName() == null ? other.getName() != null : !this.getName().equals(other.getName())) return false;
if (this.getAge() != other.getAge()) return false;
if (Double.compare(this.getScore(), other.getScore()) != 0) return false;
if (!Arrays.deepEquals(this.getTags(), other.getTags())) return false;
return true;
}
#Override public int hashCode() {
final int PRIME = 59;
int result = 1;
final long temp1 = Double.doubleToLongBits(this.getScore());
result = (result*PRIME) + (this.getName() == null ? 43 : this.getName().hashCode());
result = (result*PRIME) + this.getAge();
result = (result*PRIME) + (int)(temp1 ^ (temp1 >>> 32));
result = (result*PRIME) + Arrays.deepHashCode(this.getTags());
return result;
}
public static class Exercise<T> {
private final String name;
private final T value;
private Exercise(String name, T value) {
this.name = name;
this.value = value;
}
public static <T> Exercise<T> of(String name, T value) {
return new Exercise<T>(name, value);
}
public String getName() {
return this.name;
}
public T getValue() {
return this.value;
}
#Override public String toString() {
return "Exercise(name=" + this.getName() + ", value=" + this.getValue() + ")";
}
protected boolean canEqual(Object other) {
return other instanceof Exercise;
}
#Override public boolean equals(Object o) {
if (o == this) return true;
if (!(o instanceof Exercise)) return false;
Exercise<?> other = (Exercise<?>) o;
if (!other.canEqual((Object)this)) return false;
if (this.getName() == null ? other.getValue() != null : !this.getName().equals(other.getName())) return false;
if (this.getValue() == null ? other.getValue() != null : !this.getValue().equals(other.getValue())) return false;
return true;
}
#Override public int hashCode() {
final int PRIME = 59;
int result = 1;
result = (result*PRIME) + (this.getName() == null ? 43 : this.getName().hashCode());
result = (result*PRIME) + (this.getValue() == null ? 43 : this.getValue().hashCode());
return result;
}
}
}
Add Kotlin to your project, is becoming the standard, solves your problem as a charm, and as is officially supported by Google, you do not have any problem if you go in production, instead to use other libraries( that could have bugs).
Do not think that you cannot manage to transform all the project from Java to Kotlin, because Kotlin is 100 per cent compatible. One of the K features of Kotlin is just to solve your problem: avoiding to have constructor linked to instance variables and getter and setter, is a lot of boiler plate code.
you just add Kotlin to your project, will take less than 3 minutes, then you can change only the POJO classes, this is the name/acronym of the plain classes you are referring with constructors, getter and setters.
After you installed Kotlin, use Data Classes
in this way a class with 86 lines like the following will become one line. Is worthy to do it, even if you are not going to implement Kotlin to the rest of your project
public class Movie {
private String name;
private String studio;
private float rating;
public Movie(String name, String studio, float rating) {
this.name = name;
this.studio = studio;
this.rating = rating;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getStudio() {
return studio;
}
public void setStudio(String studio) {
this.studio = studio;
}
public float getRating() {
return rating;
}
public void setRating(float rating) {
this.rating = rating;
}
#Override
public int hashCode() {
final int prime = 31;
int result = 1;
result = prime * result + ((name == null) ? 0 : name.hashCode());
result = prime * result + Float.floatToIntBits(rating);
result = prime * result + ((studio == null) ? 0 : studio.hashCode());
return result;
}
#Override
public boolean equals(Object obj) {
if (this == obj)
return true;
if (obj == null)
return false;
if (getClass() != obj.getClass())
return false;
Movie other = (Movie) obj;
if (name == null) {
if (other.name != null)
return false;
} else if (!name.equals(other.name))
return false;
if (Float.floatToIntBits(rating) != Float.floatToIntBits(other.rating))
return false;
if (studio == null) {
if (other.studio != null)
return false;
} else if (!studio.equals(other.studio))
return false;
return true;
}
#Override
public String toString() {
return "Movie [name=" + name + ", studio=" + studio + ", rating=" + rating + "]";
}
}
will become just
this and will get for free also toHash and toString:
data class Movie(var name: String, var studio: String, var rating: Float)
Right now I have this Java code
import java.util.HashSet;
import java.util.Objects;
import java.util.Set;
public class Dummy {
private String value;
public Dummy(final String value) {
this.value = value;
}
public boolean equals(final Object that) {
return that instanceof Dummy && Objects.equals(value, ((Dummy) that).value);
}
public int hashcode() {
return Objects.hash(value);
}
public static void main(final String... args) {
final Set<Dummy> dummies = new HashSet<>();
dummies.add(new Dummy("toto"));
System.out.println(dummies.contains(new Dummy("toto")));
}
}
The output is "false", and I'm supposed to change it to "true" by changing only one character, but I have absolutely no idea how to do that... Any ideas? Thanks. :)
hashcode() is not a Object's method but hashCode() is.
public int hashcode() {
return Objects.hash(value);
}
should be
public int hashCode() {
return Objects.hash(value);
}
In my case I had lots of objects who all need a equals and hash method. I have used lombok to reduce the work and code.
#Data
#AllArgsConstructor
#EqualsAndHashCode
public class JsonPictureStuff {
private String type;
private String url;
private String width;
private String height;
}
The annotations instruct lombok to add the code for you.
your set is not working because you are implementing equals and hashcode in a not proper way...
specially because you are not even considering the string field
you are avoiding the annotation Override that will hint you about wrongly named methods like hashcode and hashCode
you can do something like
#Override
public int hashCode() {
final int prime = 31;
int result = 1;
result = prime * result + ((value == null) ? 0 : value.hashCode());
return result;
}
#Override
public boolean equals(Object obj) {
if (this == obj)
return true;
if (obj == null)
return false;
if (getClass() != obj.getClass())
return false;
Dummy other = (Dummy ) obj;
if (value == null) {
if (other.value != null)
return false;
} else if (!value.equals(other.value))
return false;
return true;
}
in ide like eclipse you can do right click and auto generate those methods so you can save the implementation
I've following the following Vertex class and it implements equals, hashCode and compareTo method. Even then my HashMap returns null. I don't know why?
public class Vertex implements Comparable<Vertex> {
int id;
public Vertex(int number) {
id = number;
}
public boolean equals(Object other) {
if (other == null)
return false;
else if (other.getClass() != this.getClass())
return false;
else {
Vertex copy = (Vertex) other;
if (copy.id == this.id)
return true;
else
return false;
}
}
public int hasCode() {
int prime = 31;
int smallPrime = 3;
int hashCode = this.id ^ smallPrime - prime * this.hasCode();
return hashCode;
}
public int compareTo(Vertex other) {
if (this.id < other.id)
return -1;
else if (this.id > other.id)
return 1;
else
return 0;
}
}
Your method is called hasCode(). Make it hashCode() instead.
I'd suggest using your IDE to automatically generate hashCode() and equals(..). That will generate the proper methods (right now you have a recursive call in hashCode())
Also, in your equals() method
else if(other.getClass()!=this.getClass())
return false;
can be changed to
else if(!(other instanceof Vertex))
return false;
Try this based on what Integer does. Note: the use of #Override would have shown you were overriding the wrong method.
public class Vertex {
final int id;
public Vertex(int number){
id = number;
}
#Override
public boolean equals(Object other){
if(!(other instanceof Vertex)) return false;
return ((Vertex)other).id == id;
}
#Override
public int hashCode() { return id; }
}
I'm looking for a suggestion.
I have a Person class with String firstName and String lastName
When i'm tying to insert the list values with the same String like :
set.add(new Person("firstName","lastName"))
set.add(new Person("firstName","lastName"))
The set doesn`t filter the objects and they still getting in the set.
There is any suggestion to create set list without overriding the equales and hashcode functions?
Maybe with guava or some groovy list?
Thanks,
Or.
In Guava there's an Equivalence class designed to such things. Create your own Equivalence class like this one:
import com.google.common.base.Equivalence;
import com.google.common.base.Objects;
public class PersonEquivalence extends Equivalence<Person> {
#Override
protected boolean doEquivalent(Person p1, Person p2) {
return Objects.equal(p1.getFistName(), p2.getFistName())
&& Objects.equal(p1.getLastName(), p2.getLastName());
}
#Override
protected int doHash(Person person) {
return Objects.hashCode(person.getFistName(), person.getLastName());
}
}
And then this code
Set<Equivalence.Wrapper<Person>> set = Sets.newHashSet();
PersonEquivalence personEquivalence = new PersonEquivalence();
set.add(personEquivalence.wrap(new Person("Joe", "Doe")));
set.add(personEquivalence.wrap(new Person("Joe", "Doe")));
set.add(personEquivalence.wrap(new Person("Jane", "Doe")));
System.out.println(set);
prints
[PersonEquivalence#8813f2.wrap(Person{firstName=Jane, lastName=Doe}),
PersonEquivalence#8813f2.wrap(Person{firstName=Joe, lastName=Doe})]
Of course it's a bit verbose, but you can create ForwardingSet to automatically wrap and unwrap Persons for you.
You can create a TreeSet with your own Comparator.
Set<Person> set = new TreeSet<Person>(new Comparator<Person>() {
#Override
public int compare(Person p1, Person p2) {
// Your own compare logic
}
});
You can't, without violating the contract of Set. Either don't use a Set, or wrap the Person in another class that implements equals and hashcode based on the inner Person (see the other answer for a way to do this in Guava).
Here's a rough attempt at my map suggestion.
import java.util.HashMap;
import java.util.Iterator;
import java.util.Map;
public class PeopleCarrier implements Iterable<Person>{
private Map<PersonKey, Person> storage = new HashMap<PersonKey, Person>();
public void add(Person p) {
PersonKey pk = new PersonKey(p);
storage.put(pk, p);
}
public boolean contains(Person p) {
return storage.containsKey(new PersonKey(p));
}
#Override
public Iterator<Person> iterator() {
return new Iterator<Person>() {
private Iterator<PersonKey> i = storage.keySet().iterator();
#Override
public void remove() {
throw new UnsupportedOperationException();
}
#Override
public Person next() {
return storage.get(i.next());
}
#Override
public boolean hasNext() {
return i.hasNext();
}
};
}
private class PersonKey {
private String firstname;
private String lastname;
public PersonKey(Person p) {
this.firstname = p.getFirstname();
this.lastname = p.getLastname();
}
/* (non-Javadoc)
* #see java.lang.Object#hashCode()
*/
#Override
public int hashCode() {
final int prime = 31;
int result = 1;
result = prime * result + getOuterType().hashCode();
result = prime * result
+ ((firstname == null) ? 0 : firstname.hashCode());
result = prime * result
+ ((lastname == null) ? 0 : lastname.hashCode());
return result;
}
/* (non-Javadoc)
* #see java.lang.Object#equals(java.lang.Object)
*/
#Override
public boolean equals(Object obj) {
if (this == obj)
return true;
if (obj == null)
return false;
if (!(obj instanceof PersonKey))
return false;
PersonKey other = (PersonKey) obj;
if (!getOuterType().equals(other.getOuterType()))
return false;
if (firstname == null) {
if (other.firstname != null)
return false;
} else if (!firstname.equals(other.firstname))
return false;
if (lastname == null) {
if (other.lastname != null)
return false;
} else if (!lastname.equals(other.lastname))
return false;
return true;
}
private PeopleCarrier getOuterType() {
return PeopleCarrier.this;
}
}
}