24

I have an address object that I want to create an equals method for. I could have made this quite simple by doing something like the following (shortened a bit):

public boolean equals(Object obj) 
{
    if (this == obj)
        return true;

    if (obj == null)
        return false;

    if (getClass() != obj.getClass())
        return false;

    Address other = (Address) obj;

    return this.getStreet().equals(other.getStreet())
        && this.getStreetNumber().equals(other.getStreetNumber())
        && this.getStreetLetter().equals(other.getStreetLetter())
        && this.getTown().equals(other.getTown());
}

Problem is, some of these might be null. I will in other words get a NullPointerException if there is no street letter in this address.

How can I write this in a clean way while taking null values into account?

user4157124
  • 2,809
  • 13
  • 27
  • 42
Svish
  • 152,914
  • 173
  • 462
  • 620
  • 1
    using the *@NotNull* annotation everywhere surely helps. So does using empty strings and empty arrays, instead of null string and null arrays to represent something that is "empty". An empty string is a string that contains no character. An empty array is an array made of zero element. "null" and NPEs are exceedingly rare in our codebase, where the *@NotNull* annotation is used in every single class we have :) – SyntaxT3rr0r Apr 14 '11 at 10:52
  • 1
    in addition to that, the concept of "equality" over mutable object doesn't make much sense. Make your objects immutable. Forbid null. Everything will be smoother. – SyntaxT3rr0r Apr 14 '11 at 10:53
  • 1
    Of course, and I would if I could! Problem is that in this case these things *can* be non-existent. For example, not all addresses have a letter, so it should be null. On the matter of immutability, I totally agree, but unfortunately it's not my choice. If it was up to me, it would be. – Svish Apr 14 '11 at 11:05

9 Answers9

18

You can use a helper method like

public static boolean isEqual(Object o1, Object o2) {
    return o1 == o2 || (o1 != null && o1.equals(o2));
}
Peter Lawrey
  • 525,659
  • 79
  • 751
  • 1,130
11

Google Guava provides Objects.equal(Object, Object) which checks for equality while taking into consideration that either of the parameters might be null:

...
return Objects.equal(this.getStreet(), other.getStreet())
    && Objects.equal(this.getStreetNumber(), other.getStreetNumber())
    && Objects.equal(this.getStreetLetter(), other.getStreetLetter())
    && Objects.equal(this.getTown(), other.getTown());

It's also worth pointing out that Objects has other helper methods for implementing hashCode() and toString().

Jared Russell
  • 10,804
  • 6
  • 30
  • 31
9

You could do the following:

public boolean equals(Object obj) 
{
    if (this == obj) {
        return true;
    }

    if (obj == null) {
        return false;
    }

    if (getClass() != obj.getClass()) {
        return false;
    }

    Address other = (Address) obj;

    return equals(this.getStreet(),other.getStreet())
        && equals(this.getStreetNumber(), other.getStreetNumber())
        && equals(this.getStreetLetter(), other.getStreetLetter())
        && equals(this.getTown(), other.getTown());
}

private boolean equals(Object control, Object test) {
    if(null == control) {
        return null == test;
    }
    return control.equals(test);
}

Java 7 introduced built-in support for this use case with the java.util.Objects class see:

Zac Thompson
  • 12,401
  • 45
  • 57
bdoughan
  • 147,609
  • 23
  • 300
  • 400
  • 1
    @Svish It will look cleaner + it would be optimum too. – jmj Apr 14 '11 at 11:11
  • 1
    @Jigar, Personally I think it looks cleaner as it is. But why would it be optimum? No matter have you look at it, you have 3 checks that potentially have to be done. And if any of them go through, you'd stop execution. Is there some compiler optimizations I don't know about or something? – Svish Apr 14 '11 at 11:13
  • 1
    @Swish Well if you use Collections and if you look for a object and if it invokes equal method for each object there would be atleast 3 * n + rest in `equal` method check performed , where if you use else if , then in worst case it would happen. – jmj Apr 14 '11 at 11:17
  • I think this is probably what I would do. Maybe make that method a static method in a utility class or something, but yeah... lot cleaner than doing all those null checks mixed in. – Svish Apr 14 '11 at 12:48
  • 2
    @Blaise: -1 to your question for not enclosing the body of if statements in parenthesis. I mean, c'mon, this 2011 not the 1960's. It is a terrible practice to leave the parenthesis out, a practice we have known for decades to increase the incidence of defects. – luis.espinal Apr 14 '11 at 12:58
  • 2
    @Jigar: if I could -neg rep your comment, I would. Why is it optimum? Both forms get compiled to the exact same bytecode. Those two sets of if (and if-else) statements are syntactically equivalent (hint, you need to learn how compilers work). Also, bringing a hypothetical collections-based scenario does not validate your argument since it is not applicable to the specific code snippet under consideration - that's poor engineering thinking. – luis.espinal Apr 14 '11 at 13:04
  • @luis: +1. Even if it was non-performant (it's not), that wouldn't matter unless a it was causing a measurable delay to the total performance of the application. Bottom line: making performance claims without measurements is a big mistake. – Sean Reilly Apr 14 '11 at 13:15
  • @luis.espinal - I agree (it is now fixed). Honestly my answer was focused on the question about the null check and I just copied and pasted from the OP and modified the piece specific to the question. – bdoughan Apr 14 '11 at 13:30
  • About the missing `{ }`, I sometimes leave them out and sometimes don't. Depends if they make things clearer or not. In general I tend to leave them out if it's a simple `return` or `throw` statement. If it's something else I tend to add them. – Svish Apr 14 '11 at 14:09
4

I have a helper class Checker w/ a static method:

 public static boolean isEquals(final Object o1, final Object o2) {
        return o1 == null ? o2 == null : o1.equals(o2);
 }

so, in the equals method,

 return Checker.isEquals(this.getStreet(), other.getStreet())
        && Checker.isEquals(this.getStreetNumber(), other.getStreetNumber())
        && Checker.isEquals(this.getStreetLetter(), other.getStreetLetter())
        && Checker.isEquals(this.getTown(), other.getTown());
kliu
  • 687
  • 1
  • 9
  • 17
2

There is no really clean way to do that; the best option is probably to have your IDE generate the code for you. Eclipse can do it via the Source -> Generate hashCode() and equals() context menu.

Michael Borgwardt
  • 342,105
  • 78
  • 482
  • 720
2

You can use Objects.equal from Googles guava or the EqualsBuilder from apache commons

moritz
  • 5,094
  • 1
  • 26
  • 33
1

You can use java.util.Objects.equals(a, b) if you want an official way (built-in).

(There is already a mention to it in one of the answers, but more as a sidenote)

Change:

this.getStreet().equals(other.getStreet())

To:

Objects.equals(this.getStreet(), other.getStreet())
Lucas Basquerotto
  • 7,260
  • 2
  • 47
  • 61
0

I'd consider defining some of the equals methods as static class methods, like say for the Street objects. This way you don't ever attempt to call the .equals() method on a null.

A sample function might look like:

public static boolean equals(Object one, Object two)

Also, it's good practice to put checks like

if (obj == null)
   return false;

at the very beginning of a function.

  • Yeah, must say I miss the static `Equals` method you have on objects in C#. Would be handy in this case. – Svish Apr 14 '11 at 11:08
0

Apache Commons Lang provides the EqualsBuilder helper class for equality comparissons. There is also one for hash codes.

return new EqualsBuilder()
.append(this.getStreet(), other.getStreet())
.append(this.getStreetNumber(), other.getStreetNumber()
.append(this.getStreetLetter(), other.getStreetLetter())
.append(this.getTown(), other.getTown())).isEquals();
Ian Gilham
  • 1,916
  • 3
  • 20
  • 31