How to override equals without increasing cyclomatic complexity? - java

I was recently overriding some equals methods in domain objects of my recent Java project. As we are using Sonar to calculate our code metrics, I immediately saw the cyclomatic complexity of these classes increasing above a threshold.
I'm wondering if there is a clever way, pattern or option at all to keep this metric low although having a little more complex equals method.
EDIT: Here is one of my examples that I have, nothing really specific I would say, just so that we know what we are talking about.
#Override
public boolean equals(Object o) {
if (o instanceof MyKey) {
MyKey other = (MyKey) o;
if (this.foo.longValue() == other.getFoo().longValue() &&
this.bar.equalsIgnoreCase(other.getBar()) &&
this.foobar.shortValue() == other.getFoobar().longValue()){
return true;
}
}
return false;
}
#Override
public int hashCode() {
int hash = 3;
hash = 53 * hash + foo.hashCode();
hash = 53 * hash + bar.hashCode();
hash = 53 * hash + foobar.hashCode();
return hash;
}

You could use Apache's EqualsBuilder:
public boolean equals(Object obj) {
if (obj == null) { return false; }
if (obj == this) { return true; }
if (obj.getClass() != getClass()) {
return false;
}
MyClass rhs = (MyClass) obj;
return new EqualsBuilder()
.appendSuper(super.equals(obj))
.append(field1, rhs.field1)
.append(field2, rhs.field2)
.append(field3, rhs.field3)
.isEquals();
}

You didn't but you should always check for nulls. foo could be null, resulting in a NullPointerException.
this.foo.longValue() == other.foo.longValue()
Luckily Objects utility class saves you from a lot of problems as it automatically checks for nulls.
#Override
public boolean equals(Object object) {
if (object == null)
return false;
if (!(object instanceof MyObject))
return false;
MyObject other = (MyObject) object;
//#formatter:off
return Objects.equals(getX(), other.getX()) &&
Objects.equals(getY(), other.getY()) &&
Objects.equals(getZ(), other.getZ()));
//#formatter:on
}
#Override
public int hashCode() {
return Objects.hashCode(getX(), getY(), getZ());
}
If the fields to check are a lot you can optionally add this at the beginning of the equals method.
if (object == this)
return true;
In theory it can save some computation in some edge case.
The only thing that really helps, in my opinion, is good indentation. I always wrap those line between a pair of //#formatter:off and //#formatter:on. It's boilerplate code, anyway: very easy to write, very easy to make mistakes.
In your case, though, you're checking equality using equalsIgnoreCase. It's a pity Objects doesn't have such a method. You can build your own pretty easily.
public final class Strings {
public static boolean equalsIgnoreCase(String a, String b) {
return a == null ? b == null : a.equalsIgnoreCase(b);
}
private Strings() {
}
}
And use it like this
return Objects.equals (getX(), other.getX()) &&
Strings.equalsIgnoreCase (getY(), other.getY()) &&
Objects.equals (getZ(), other.getZ()));

Related

How can I avoid multiple if else blocks for Object.equals method?

I'm trying to implement this:
public boolean equals(Object o) {
if (o == this) {
return true;
}
if ((null == o) || !(o instanceof Document)) {
return false;
}
Document other = (Document) o;
// compare in a null-safe manner
if (list == null) {
if (other.list != null)
return false;
} else if (other.list == null)
return false;
else if (!(list.size() == other.list.size())
&& !(list.equals(other.list)))
return false;
return true;
where 'list' is a class variable as well as a field of the object 'o'. Please note that the object 'o' has many other fields including booleans and collection too and I need to compare all of them.
I tried finding related answers but most of them recommend switch cases or other Java 8 components which is not relevant to my scenario.
You're greatly complicating things. Writing "equals" is lengthy boring boilerplate, but you're making it even longer.
public boolean equals(Object o) {
if (o == this) {
return true;
}
if (!(o instanceof Document)) {
return false;
}
Document other = (Document) o;
if (!Objects.equals(list, other.list)) {
return false;
}
return true;
}
All you need per reference field are the three lines above, similarly for primitives (don't forget to handle NaN for floating point).
Your condition is not only much longer, but it also lacks symmetry. This makes it much harder to write and more error-prone.
Anyway, writing "equals" is not something you should do often manually. I recommend using Lombok and there are many more tools, e.g., AutoValue or EqualsBuilder.
A direct rewrite would be:
// compare in a null-safe manner
if (list == null || other.list == null) {
return list == other.list;
} else {
return list.size() == other.list.size() ||
list.equals(other.list));
}
except if the type of list is a standard Java SE List class you can do away with the size() micro-optimization. (A typical `List.equals implementation will do that for you.) So we can rewrite the above as
// compare in a null-safe manner
if (list == null || other.list == null) {
return list == other.list;
} else {
return list.equals(other.list));
}
except that that is what Objects.equals(...) does. So the final rewrite is:
// compare in a null-safe manner
return Objects.equals(list, other.list);
It is unclear if an IDE will generate equals methods test the fields in a null-safe way. But the counterpoint to that is that it is advisable to design your classes so that you don't need to do that. For example, use an empty List rather than a null.

get() and contains() methods do not find object in HashSet although it can be found by iterating and using equals() and the hashcode is the same

I have a program which first generates a Hashmap with all allowed instances of a particular object, called BoardState, as the keys. I then iterate over the keyset, creating copies of the BoardState objects and performing transformations on them and then looking up the transformed objects in my statemap and updating their associated values. The problem is that when I use the contains() method on this keyset (either directly or by first creating a HashSet of the keys) it will sometimes return false for my new object even though the object does exist in the map.
I know that the obvious answer here is that there's something wrong with my implementation of either equals() or hashcode() in BoardState or one of its fields, and I would be inclined to agree. In fact I have been able to narrow the problem down somewhat. BoardState includes as an instance variable a HashSet of Box objects, which I also implemented, and setting the hashcode() method of Box to return a constant resolves the issue (though this obviously is not an acceptable solution).
The thing is, that when I am getting the error I can still iterate through my keyset and find the object by comparing using equals(). If I then output the hashcode for this object and the object I am checking against I get the same result for each, so I'm at a loss as to why it is that contains() is throwing an error.
I apologise if the below code is a bit meaty, I've tried to strip out what I can and only show what's relevant to the error.
public class BoardState {
private static int size;
private static int totalTokens;
private static HashMap<Colour, Integer> colours;
private static HashSet<Token> fullTokenSet;
private int inactiveBoxes;
private HashSet<Box> boxes;
private HashSet<Token> offBoardTokens;
public BoardState(...){...}
public boolean checkRemoveBox(final Box box,
final HashMap<BoardState, Boolean> stateMap) {
BoardState checkState = copy();
checkState.remove(box, box.getBoxColours());
if (!stateMap.keySet().contains(checkState)) {
for (BoardState state : stateMap.keySet()) {
if (state.equals(checkState)){
System.out.println("state hashcode: " + Objects.hash(state));
System.out.println("checkstate hashcode: " +
Objects.hash(checkState));
}
}
throw new IllegalStateException ("State not found.");
} else {
if (!stateMap.get(checkState)) {
return false;
}
}
return true;
}
#Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
BoardState state = (BoardState) o;
return size == state.size &&
inactiveBoxes == state.inactiveBoxes &&
totalTokens == state.totalTokens &&
boxes.equals(state.boxes) &&
fullTokenSet.equals(state.fullTokenSet) &&
offBoardTokens.equals(state.offBoardTokens) &&
colours.equals(state.colours);
}
#Override
public int hashCode() {
return Objects.hash(inactiveBoxes, boxes, offBoardTokens);
}
}
public class Box {
private static int totalTokens;
private HashSet<Token> tokens;
Box(...) {...}
#Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
Box box = (Box) o;
return totalTokens == box.totalTokens &&
Objects.equals(tokens, box.tokens);
}
#Override
public int hashCode() {
return tokens.hashCode();
}
}
The given code gives the following output:
state hashcode: 157760
checkstate hashcode: 157760
Exception in thread "main" java.lang.IllegalStateException: State not found.
at game.BoardState.checkSplitBox(BoardState.java:306)
at game.BoardState.checkSplit(BoardState.java:284)
at game.Game.checkForP1Win(Game.java:173)
at game.Main.main(Main.java:11)
Process finished with exit code 1

Using String's equals() implementation inside another equals() implementation?

Let's say a class Member where member has an Id of type String. I want to know if there might be any problem with using a String equals() implementation inside another implementation. Would it be any better if the field Id was of type Long.
#Override public boolean equals(Object object){
if(object == null) return false;
if(! (object instanceof Member)) return false;
Member member= (Member) object;
if(this.Id.equals(member.Id)) //<==My concern is here
return true;
else
return false;
}
The only problem I see is that you may have more class members besides id and your equals implementation will say true even while two instances differ greatly but have the same id. With this in mind, make sure to have a consistent hashCode implementation alongside to avoid inconsistencies.
Another idea is to define custom comparators e.g.
static Comparator<Member> MEMBER_ID_COMPARATOR = new Comparator<Member>() {
#Override
public int compare(Member first, Member second) {
assert(first.getId() != null);
assert(second.getId() != null);
return first.getId().compareTo(second.getId());
}
}
No problem at all. From this point of view, leave Id as a String. If it's only going to contain numbers, yes, you can use Long or BigInteger (my preference). But this is a different animal whatsoever.
Try this instead
#Override public boolean equals(Object object){
if(object == null) return false;
if(! (object instanceof Member)) return false;
Member member= (Member) object;
if (this.Id == null && member.Id == null)
return true;
else if (this.Id != null && member.Id != null)
return this.Id.equals(member.Id);
else
return false;
}

Java: howto write equals() shorter

I get headaches when I have to write nearly 10 lines of code to say 2 Objects are equal, when their type is equal and both's attribute is equal. You can easily see that in this way of writing the number of lines increase drastically with your number of attributes.
public class Id implements Node {
private String name;
public Id(String name) {
this.name = name;
}
public boolean equals(Object o) {
if (o == null)
return false;
if (null == (Id) o)
return false;
Id i = (Id) o;
if ((this.name != null && i.name == null) || (this.name == null && i.name != null))
return false;
return (this.name == null && i.name == null) || this.name.equals(i.name);
}
}
Google's guava library has the Objects class with Objects#equal that handles nullness. It really helps get things smaller. With your example, I would write:
#Override public boolean equals(Object other) {
if (!(other instanceof Id)) {
return false;
}
Id o = (Id) other;
return Objects.equal(this.name, o.name);
}
The documentation is here.
Also note that there is Objects#hashCode and Objects#toStringHelper to help with hashCode and toString as well!
Please also see Effective Java 2nd Edition on how to write equals().
If you use Eclipse, click "Source" -> "generate hashCode() and equals()". There're many options to create equals() automatically.
There are libraries that'll do it for you. For example, commons-lang has EqualsBuilder
Also, these two lines appear to do the same thing:
if (o == null)
return false;
if (null == (Id) o)
return false;
Maybe you meant this:
if (o == null)
return false;
if (this == o)
return true;
Project Lombok also has a equals and hashCode generator using the #EqualsAndHashCode annotation which has the advantage of being in sync with the current class/source code. I'm not sure about the implementation details but definitely worth looking into if you need to cut down the cruft.
A simpler way (other than generating the code) might be.
public boolean equals(Object o) {
return o instanceof Id
&& (name == null ? ((Id)o).name == null : name.equals(((Id)o).name);
}

An efficient equals(Object o) implementation

I read this SO post after I wrote out the title but still decided to go through with the question on bug-proof implementations of equals in Java. This is my normal implementation
#Override
public boolean equals(Object o){
if(o == null) return false;
if(o instanceof CompositePk == false) return false;
if(this == o) return true;
CompositePk that = (CompositePk)o;
return new EqualsBuilder().append(this.id, that.id)
.append(this.bucketId, that.bucketId)
.isEquals();
}
using Apache's EqualsBuilder to do the mundane stuff. Even easier than this is my Netbean's automatically generated equals(o) implementation
#Override
public boolean equals(Object obj){
if (obj == null) {
return false;
}
if (getClass() != obj.getClass()) {
return false;
}
final TemplatesWrapper other = (TemplatesWrapper) obj;
if (this.timeAdded != other.timeAdded && (this.timeAdded == null || !this.timeAdded.equals(other.timeAdded))) {
return false;
}
return true;
}
I take these from 2 diff projects but they both try to accomplish the same thing but using diff approaches. Which style would you rather or are there any flaws you spot?
First of all, there's no need to test for null, then test for instanceof, since foo instanceof Bar evaluates to false when foo is null.
It's weird to compare the result of the instanceof operator to false, since instanceof is a boolean operation.
Comparing classes with getClass() is at best controversial. Joshua Bloch, who wrote much of the Java collections framework and a lot of other important stuff besides, says
This technique ("getClass-based equals
methods") does satisfy the equals
contract, but at great cost. The
disadvantage of the getClass approach
is that it violates the "Liskov
Substitution Principle," which states
(roughly speaking) that a method
expecting a superclass instance must
behave properly when presented with a
subclass instance. If a subclass adds
a few new methods, or trivially
modifies behavior (e.g., by emitting a
trace upon each method invocation),
programmers will be surprised when
subclass and superclass instances
don't interact properly. Objects that
"ought to be equal" won't be, causing
programs to fail or behave
erratically. The problem is
exacerbated by the fact that Java's
collections are based on the equals
method.
You should use instanceof instead of comparing via getClass() unless you have some specific technical reason not to.
After establishing that the other object is comparable to this, you then compare primitives with == and objects with equals. It's more complicated if any of your member objects can be null; you must then write verbose clauses to compare possibly null things to each other (or write a bothNullOrEqual(Object a, Object b) method).
The EqualsBuilder approach looks bogus to me, but that's just a "smell", which I won't argue against technically. In general, I don't like extra method calls in a method that may be called frequently.
The Apache one is bogus because it tests for null and uses the getClass() comparison.
Here's mine:
#Override
public boolean equals(final Object o) {
if (!(o instanceof MyClass))
return false;
final MyClass om = (MyClass)o;
// compare om's fields to mine
}
I would do it this way:
public boolean equals(Object ob) {
if (ob == null) return false;
if (ob == this) return true;
if (!(ob instanceof MyClass)) return false; // OR
if (ob.getClass() != getClass()) return false;
// check relevant members
}
The two lines in the middle are different. One allows for subclasses to be equal (the first one), the other doesn't. Use whichever one is appropriate.
To give you an example, Java's AbstractList class will probably use the second form, because the exact implementation of List is irrelevant. what matters is if the members are equal and in the same position.
Conversely, a Person class should use the first form (instanceof) because if there is a Student subclass and you call Person.equals(Student) it may return true without checking the extra fields in Student whereas Student.equals(Person) will probably return false. If equals() isn't commutative, you're asking for trouble.
I tend to use equals() methods generated by my IDE (IntelliJ IDEA) rather than creating an unnecessary dependency to some Apache library for little gain.
Apache's is better than yours or cletus'.
As far as my vague memory suggests, there is a problem with using instanceof in equals; I can't quite put my finger on why yet, perhaps someone will elaborate. I could be wrong.
-- Edit:
As Chris and Steve helpfully explain below, I was thinking of the "symmetric property" of equals implementation. On this basis, I can back up my claim of prefering the Apache implementation :)
Honestly, the less code you have to write, the better off you are (in most cases).
The code that's generated has been debugged and used by many MANY people. You might as well use what's generated (and if you need to enhance the performance, do so).
The advantage of using the generated code: any time your instance fields changes (and this generated code wasn't modified), you can simply regenerate code.
Sometimes, it's easier to think about maintainability. Rule of thumb: the less code you write yourself, the less you have to debug. If the generated code doesn't take a huge performance hit, generate it!
Explanation: When overriding the equals method, the hashCode() method must be overrided too. So, considering a class with 3 properties as show below and considering that all the properties are significant to equality, the equals() implementation must test all these fields. The order of conditionals isn't important, but all the fields must to be tested for equality to consider the equality between objects at all.
public class SampleClass {
private Long id;
private String description;
private Date creation;
#Override
public int hashCode() {
final int prime = 31;
int result = 1;
result = prime * result + ((creation == null) ? 0 : creation.hashCode());
result = prime * result + ((description == null) ? 0 : description.hashCode());
result = prime * result + ((id == null) ? 0 : id.hashCode());
return result;
}
#Override
public boolean equals(Object obj) {
boolean isEquals = true;
if (this == obj) { isEquals = true; }
else if (obj == null) { isEquals = false; }
else if (getClass() != obj.getClass()) { isEquals = false; }
else {
SampleClass other = (SampleClass) obj;
if (creation == null) {
if (other.creation != null) isEquals = false;
} else if (!creation.equals(other.creation)) {
isEquals = false;
} else if (description == null) {
if (other.description != null) isEquals = false;
} else if (!description.equals(other.description)) {
isEquals = false;
} else if (id == null) {
if (other.id != null) isEquals = false;
} else if (!id.equals(other.id)) {
isEquals = false;
}
}
return isEquals;
}

Categories

Resources