18

I have always understood there to be two types of equality in Java,

  • value equality : uses the .equals() method to test that two objects implement an equivalence relation on non-null object references.
  • reference equality : uses the == operator to test that two primitive types or memory locations are equal.

The following pages describe these language fundamentals in more detail.

What none of these links explicitly specify is what should happen if two null object references are compared for value equality. The implicit assumption is that a NullPointerException should be thrown but this is not what is done by the ObjectUtils.equals() method, which might be considered a best practice utility method.

What worries me is that Apache Commons seems to have effectively introduced a third measure of equality into Java by the back door and that the already confusing state of affairs might have been made greatly more complex. I call it a third measure of equality because it attempts to test for value equality and when that fails it falls back to testing for reference equality. The Apache Commons equality test has many similarities with the value equality and reference equality but is also distinctly different.

Am I right to be concerned and to want to avoid using the ObjectUtils.equals() where ever possible?

Is there an argument for claiming that ObjectUtils.equals() provides a useful union of the other two measures of equality?

Chosen Answer

There doesn't seem to be a consensus opinion on this question but I decided to mark Bozho's as correct because he best drew my attention to what I now see as the greatest problem with null-safe equals checks. We should all be writing fail-fast code that addresses the root cause of why two null objects are being compared for value equality rather than trying to sweep the problem under the carpet.

Duncan Jones
  • 67,400
  • 29
  • 193
  • 254
Caoilte
  • 2,381
  • 1
  • 21
  • 27
  • 1
    +1 for a well-researched question – trashgod Jan 25 '10 at 15:40
  • Can you explain how this is a third measure of equality? I think comparing two null "memory locations" (sic) and getting true is no different than comparing two primitive 0 values and getting true. NullPointerException is for attempting to dereference a null pointer... entirely different than just checking its value. – PSpeed Jan 25 '10 at 18:30
  • I have updated the question to explicitly state how the Apache Commons equality test differs from the two Java methods of testing equality, but the problem your assumption ignores is more nuanced. When I test for value equality I am not comparing two null memory locations. The JLS states this quite explicitly. That memory locations are _sometimes_ compared anyway is an implementational detail/shortcut. It is not specified and in the case where both objects are null it is inappropriate. Null is not an object in java. – Caoilte Jan 28 '10 at 13:20
  • But what's unclear is why comparing two things that "are not and object" should not be equal. It seems logical to return true in this case... especially when the most likely comparison is the field values of two objects. Object A .equals() Object B if A.foo == B.foo even if foo is pointing to "not an object". – PSpeed Jan 28 '10 at 17:53

3 Answers3

11

Here's the code of ObjectUtils.equals(..):

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

ObjecUtils docs state clearly that objects passed can be null.

Now on the matter whether true should be returned if you compare two nulls. In my opinion - no, because:

  • when you compare two objects, you are probably going to do something with them later on. This will lead to a NullPointerException
  • passing two nulls to compare means that they got from somewhere instead of "real" objects, perhaps due to some problem. In that case comparing them alone is wrong - the program flow should have halted before that.
  • In a custom library we're using here we have a method called equalOrBothNull() - which differs from the equals method in this utility in the null comparison.
Bozho
  • 588,226
  • 146
  • 1,060
  • 1,140
  • If both objects are null, it returns true. – Michael Borgwardt Jan 25 '10 at 15:44
  • This is incorrect. `System.out.println("Is null == null? " + (null == null));` prints `true` – matt b Jan 25 '10 at 15:46
  • I agree that the ObjecUtils docs are quite clear about what they do. My concern with them was more that they don't warn you that their definition of equality is different to the two in the JLS. I was also most worried by that such a "loose" equals check obscures underlying errors – Caoilte Jan 25 '10 at 17:22
  • But it isn't different than the JLS, is it? String foo = null; Integer bar = null; System.out.println( foo == bar ); You should see "true". – PSpeed Jan 25 '10 at 18:24
  • It's different because it isn't "ObjectUtils.==" it's "ObjectUtils.equals()" which implies a test for value equality that should throw a NullPointerException. Even if it were "ObjectUtils.==" that would still be misleading because a test for reference equality shouldn't be calling the .equals() method of object parameters. – Caoilte Jan 28 '10 at 13:24
  • 1
    If A.equals(B) is false and A == B is true then there are big big problems. But checking A == B can be thousands of times faster in some cases when the references actually are the same. And I can't think of a single use-case for requiring the caller to do a null check first... and yet every use-case I've ever needed this sort of util method for has required the ability to pass nulls. – PSpeed Jan 28 '10 at 17:58
  • 1
    @Bozho can you consider adding a reference to Springs [nullSafeEquals](http://static.springsource.org/spring/docs/3.0.x/api/org/springframework/util/ObjectUtils.html#nullSafeEquals%28java.lang.Object, java.lang.Object%29 – Adam Gent Aug 21 '12 at 01:52
  • The question is about apache commons, but I upvoted your comment, so that people see it more easily – Bozho Aug 22 '12 at 09:07
  • @Bozho, Can we use it instead of Equalsbuilder ? – kaushik Aug 15 '16 at 11:19
5

Am I right to be concerned and to want to avoid using the ObjectUtils.equals() where ever possible?

No. What you need to consider equals depends on your requirements. And wanting to consider two nulls equal and any non-null unequal to a null without having to deal with NullPointerExceptions is a very, very common requirement (e.g. when you want to fire value-change events from a setter).

Actually, it's how equals() in general should work, and typically, half of that behvaiour is implemented (the API doc of Object.equals() states "For any non-null reference value x, x.equals(null) should return false.") - that it doesn't work the other way round is mainly due to technical restrictions (the language was designed without multiple dispatch to be simpler).

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

If you are concerned about this then you could either 1) not use this method 2) write your own to wrap it

public class MyObjectUtils {
    public static boolean equals(Object obj1, Object obj2) {
        return obj1 != null && obj2 != null && ObjectUtils.equals(obj1, obj2);
    }
}

To me it seems weird to allow for null to be equals to null, but this doesn't seem like a large problem. For the most part, I wouldn't expect my application to even get into code paths that involve equality tests if one or more objects are null.

matt b
  • 138,234
  • 66
  • 282
  • 345
  • well, (as I said in my initially wrong statement), `(null == null) = true` may be a good source for NPE later on. – Bozho Jan 25 '10 at 15:50
  • Let me rephrase, it seems weird to me to ever need to test two references that are both null for equality – matt b Jan 25 '10 at 15:52
  • Let's see... places I've compared a null to a null: nearly every .equals() method I've ever written for objects with non-primitive fields, every PropertyChangeEvent dispatch I've ever written, ... man, it just comes up so often. – PSpeed Jan 25 '10 at 18:27
  • Of course there easy work arounds. My concern was that as a best practice utility library, Apache Commons has an ability to influence developers and that in this case the result could be negative to the quality of downstream code. I work on many different code bases for many different clients and many use Apache Commons wherever they can without stopping to think whether they should. – Caoilte Jan 28 '10 at 13:03
  • 1
    Utility code is meant to lessen the amount of cutting and pasting one must do. In this case, 100% of the use-cases I've ever had for such a utility method would have required me to add an additional check. Actually, on top of the example method above I'd also have to add a obj1 == obj2 (which covers my other case) because sometimes it is 1000x faster when the references are the same. – PSpeed Jan 28 '10 at 17:56