3

What is the best way to override equals method in java to compare more than one field? For example, I have 4 objects in the class, o1, o2, o3, o4 and I want compare all of them with the passed object to the equals method.

if (o1 != null && o2 != null && o3 != null && o4 != null && obj.o1 != null
    && obj.o2 != null && obj.o3 != null && obj.o4 != null
    && o1.equals(obj.o1) && o2.equals(obj.o2) && o3.equals(obj.o3) && o4.equals(obj.o4)) {
    do something
}

The problem with this code is that it's not clear and can't be modified easily specially if we have more fields. Is there a better way to achieve that?

K''
  • 5,020
  • 7
  • 33
  • 43

6 Answers6

11

A cheap way would be to do:

Arrays.asList(o1, o2, o3, o4).equals(Arrays.asList(obj.o1, obj.o2, obj.o3, obj.o4));
Mark Peters
  • 80,126
  • 17
  • 159
  • 190
  • Excellent suggestion, Mark Peters. I was going to suggest that if he has a "list" of items, then he ought to organize them into some kind of container, then iterate over the container. You've given a great, succinct example of precisely that. – paulsm4 Aug 18 '11 at 19:18
  • 1
    It's clever, and I like it, but I'm not sure I like it for this scenario. I especially don't care for needing to add it in both lists, and worrying about order. Perhaps a utility method `private List comparisonList` would help make sure you don't add a new member to one but not to the other, or add them in the wrong order. – corsiKa Aug 18 '11 at 19:21
  • nice solution, however you still need to add the null check before doing that cause List.equals will be true if both objects are null – K'' Aug 18 '11 at 19:30
  • 1
    @glowcoder: I think you have a great point; I posted with the intent of adding more later but got sidetracked by real work :-). Some of the worries here are, as you said, the lack of order checking and how easy it would be to make a mistake. The `comparisonList` is a nice way to avoid that. There are also many great libraries available that help with writing equals methods; Apache Commons' `EqualsBuilder` is an example (but that only helps with type safety, you can still make an ordering mistake). `Arrays.asList` is just a lazy man's solution :-). – Mark Peters Aug 18 '11 at 21:23
  • 1
    @Akram: I'm not sure which null check you're talking about, but if the two corresponding fields are null then *shouldn't* they be considered equal with respect to that aspect? I guess I didn't look closely at your implementation but typically an `equals` method comparing two objects that have corresponding fields set to null will consider those objects equal. – Mark Peters Aug 18 '11 at 21:24
  • 1
    At the very least, though, that is the kind of silly bug that should get caught during even the most elementary of automated tests. Even without the help of the comparison list or the equals builder or some other utility, that bug shouldn't last for very long. – corsiKa Aug 18 '11 at 21:26
9

One thing is to have a helper method in a utility class:

public static boolean equals(Object o1, Object o2)
{
     if (o1 == o2)
     {
         return true;
     }
     if (o1 == null || o2 == null)
     {
         return false;
     }
     return o1.equals(o2);
}

Then you can write:

public boolean equals(Object other)
{
    if (other == null || this.getClass() != other.getClass())
    {
        return false;
    }
    Foo x = (Foo) other;
    return Helper.equals(o1, x.o1) &&
           Helper.equals(o2, x.o2) &&
           Helper.equals(o3, x.o3) &&
           Helper.equals(o4, x.o4);
}

Note that this way it also copes when two fields are both null, which the code in the question doesn't. (I say "copes" - it gives a result which is more consistent with the rest of Java.)

You can create a similar helper method for hashCode too.

Note that Guava already supports this in its Objects class (and I'm sure many other utility libraries do too).

Jon Skeet
  • 1,421,763
  • 867
  • 9,128
  • 9,194
  • This is indeed a more efficient way than the Arrays.asList().equals approach. If o1 and x.o1 are not equal (which may happen quite often), the other fields o2, o3, o4 are not used at all, while Arrays.asList() fills the list with all fields in any case. – fgwe84 Feb 06 '15 at 08:41
5

Use a utility method to help you

private static boolean nullSafeEquals(Object o1, Object o2) {
    if(o1 == null && o2 == null) return true; // both null, both equal
    if(o1 == null || o2 == null) return false; // if one is null, not equal - we know both won't be null
    return o1.equals(o2);
}

public boolean equals(Object o) {
    if(o instanceof ThisClass) {
        ThisClass tc = (ThisClass)o;
        return nullSafeEquals(o1, tc.o1)
            && nullSafeEquals(o2, tc.o2)
            && nullSafeEquals(o3, tc.o3)
            && nullSafeEquals(o4, tc.o4);
    }
    return false;
}
corsiKa
  • 81,495
  • 25
  • 153
  • 204
  • I think you can get away with this for the `nullSafeEquals` method: `return (o1 == o2) || (o1 != null && o1.equals(o2));`. – Ted Hopp Aug 18 '11 at 19:27
  • @Ted True. I had separated it out for a bit of commentary, but I do like `o1 == o2` for the first argument better for sure. A micro-optimization for sure, but one I like! :) – corsiKa Aug 18 '11 at 19:31
5

Use Guava:

@Override
public boolean equals(final Object obj){
    if (!(obj instanceof Foo)) {
        return false;
    }
    final Foo other = (Foo) obj;
    return Objects.equal(o1, other.o1)
        && Objects.equal(o2, other.o2)
        && Objects.equal(o3, other.o3)
        && Objects.equal(o4, other.o4);
}

You get hashCode() for cheap, too:

@Override
public int hashCode() {
    return Objects.hashCode(o1, o2, o3, o4);
}
Ryan Stewart
  • 126,015
  • 21
  • 180
  • 199
0

If using one let you ide create it for you.

Tom
  • 43,583
  • 4
  • 41
  • 61
  • OP states "The problem with this code is that it's not clear and can't be modified easily specially if we have more fields. Is there a better way to achieve that?" If there's one property auto-generated code has, it is being not clear. – corsiKa Aug 18 '11 at 20:26
0

I've run into this a few times and the easiest way I did this was to put all the compared Objects into a LinkedList and then write a customer comparer method for your Object that will take in that list and run through it to find out if they are equal to the passed object. You might even find that LinkedList method 'contains()' will do this for you, depending on your Objects.

JPM
  • 9,077
  • 13
  • 78
  • 137