5

Equality is supposed to be symmetric, right?

Object someObject = new Object();
Object NULL = null;

NULL.equals(someObject) => NullPointerException
someObject.equals(NULL) => false

What is the rationale for not having the second form throw a NullPointerException?

fredoverflow
  • 256,549
  • 94
  • 388
  • 662
  • 2
    The statement `null.equals(someObject)` can't be compiled, at least by me. How did you manage to compile it? – Paul Jul 14 '11 at 18:12
  • possible duplicate of [Is it a bad idea if equals(null) throws NullPointerException instead?](http://stackoverflow.com/questions/2887761/is-it-a-bad-idea-if-equalsnull-throws-nullpointerexception-instead) –  Jul 14 '11 at 18:14
  • @Paul: It was obviously pseudo-code. Better now? :) – fredoverflow Jul 14 '11 at 18:32
  • @Fred, Not really. If you take 2 seconds and read the API for [`Object.equals()`](http://download.oracle.com/javase/6/docs/api/java/lang/Object.html#equals(java.lang.Object)) you'll see that the "equals method implements an equivalence relation on non-null object references." You're asking "why doesn't Java behave like I think it should instead of how it's defined in the API?" It might help if you read up on the definition of null. I link to the Java language spec in my answer below. – Paul Jul 14 '11 at 19:29
  • @Paul: If "equals method implements an equivalence relation on non-null object references", then passing null should *definitely* throw an exception, because null is not non-null. – fredoverflow Jul 14 '11 at 19:36
  • @Paul: I'm not asking why Java behaves as described in the API. I'm wondering why the API is defined like that in the first place. So far I haven't seen any convincing argument that throwing a `NullPointerException` when passing `null` to `equals` would be fundamentally wrong. – fredoverflow Jul 14 '11 at 19:44
  • @Fred, you haven't made a convincing argument why it should throw an NPE. I've explained why it wouldn't - the value of your object is not equal to the literal null reference. – Paul Jul 14 '11 at 19:57
  • @Paul: The argument is symmetry. I want `a.equals(b)` and `b.equals(a)` to mean the same. – fredoverflow Jul 14 '11 at 20:00
  • @Fred, I think you're hung up on the idea that null is a value. It's not. It represents the null reference: a pointer to nothing. `equals` is for comparing values; `==` when used with Objects compares references. As a bad analogy, it's like you're saying "I want my apple peeler to peel this orange". The apple peeler tool is for apples, just as `equals` is for values. Neither object nor primitive can have the value null; objects can have a reference of null. – Paul Jul 14 '11 at 20:22
  • @Paul: It seems you agree with me, after all: if equals is for comparing values, and null is not a value, then passing null to equals should be an error, right? – fredoverflow Jul 14 '11 at 20:45
  • 1
    @Fred, I don't agree at all and having equals thrown an exception would be horrible to program with. When calling `equals()` I only care about true and false. In years of programming I've never been concerned with the 3rd state: undefined (see [three-valued logic](http://en.wikipedia.org/wiki/Ternary_logic)). Do you want to wrap every call to equals in a try-catch block or check for null before calling `equals`? I sure the hell don't. If you need a 3-valued equals (which is what you're advocating...true, false, exception) then just check for null before you call equals or write an interface. – Paul Jul 14 '11 at 21:06
  • @Fred: Null is a value albeit an empty value (actually binary 0 at the lowest form of implementation). –  Jul 14 '11 at 21:09
  • @Paul: "having equals thrown an exception would be horrible to program with. [...] Do you want to wrap every call to equals in a try-catch block or check for null before calling equals?" But I have to do the null check for the reference on the left of the dot, anyway :) If you can show me a real world code snippet that benefits from equals(null) returning false instead of throwing an exception, I will upvote and accept your answer. – fredoverflow Jul 15 '11 at 06:17
  • @Fred, every time I've ever used equals benefits from null->false. When I use equals I want to know 1 of 2 things: are they equal or not equal. null->false meets that need. I never want to know one of three things: are they equal, not equal, or undefined. What case can you present where you need to know the 3rd state? – Paul Jul 15 '11 at 15:48

6 Answers6

4

In the first case, the equals() method doesn't throw the NPE, so you can't make that argument. Symmetry is part of the contract of the equals() method.

Ryan Stewart
  • 126,015
  • 21
  • 180
  • 199
  • Symmetry is part of the contract only for non-null values. – Paul Jul 14 '11 at 19:21
  • @Paul: True enough, however the question is *why* is that the case, and I'm pointing out that the premise of the question is flawed anyway because equals() didn't throw the NPE. – Ryan Stewart Jul 14 '11 at 19:41
3

Equality is certainly defined to be symmetric in a theoretical sense, but it also isn't defined at all on non-existent objects (which is what null represents).

Hence any behaviour when applied to null would be equally valid. It could return a live rabbit and still not contradict the theoretical definition of equality.

In such a case, it's a pretty reasonable implementation decision on behalf of the designers of Java that calling equals on a null value should throw a NullPointerException, as that is consistent with calling any other method on a null value.

mikera
  • 105,238
  • 25
  • 256
  • 415
  • You would think that the exception would be different or that it wouldn't compile at all. –  Jul 14 '11 at 18:03
  • @0A0D - I think this is just a practical matter of being consistent - you want any dereference of a null pointer to throw a NullPointerException at runtime otherwise people will get confused about the possible causes.... – mikera Jul 14 '11 at 18:09
  • I can see that. It just seems funny since it explicitly states that it is only in regards to non-null references. Since null is explicitly null here, one would think that the compiler would stop immediately. The value is already known prior to run-time. –  Jul 14 '11 at 18:10
1

Because you are not accessing a method of a null object in the second case. It's not the concept of equality that is unbalanced it's how you are accessing it.

zellio
  • 31,308
  • 1
  • 42
  • 61
0

The second example is not an example of symmetry because it violates one of the simple rules in regards to Object and the equals() method:

For any non-null reference value x, x.equals(null) should return false.

  • Okay, but *why* is the contract defined like that? What do we gain from this asymmetry? Why doesn't it say "`x.equals(null)` should throw a `NullPointerException`"? – fredoverflow Jul 14 '11 at 18:09
  • @FredOverflow: Because NullPointerException is a run-time exception and it does not make sense to throw an exception for something *exceptional* when it is following the rules of the Object.equals() contract. –  Jul 14 '11 at 18:13
  • @Fred, `null` is not a value so an Object cannot equal null any more than you can assigne `null` to a primitive. When you do the check `if (x == null)` you're really checking the address the pointer references. – Paul Jul 14 '11 at 18:16
  • So the contract is defined not to throw an exception when comparing to null, because if comparing to null did throw an exception, it would violate the contract? That's not a very convincing argument. I could just as well say that if the contract were defined to throw an exception on null, then trowing that exception would comply perfectly fine with the contract. – fredoverflow Jul 14 '11 at 19:42
  • @FredOverflow: It wouldn't make sense for NullPointerException because you are not dereferencing a null pointer. –  Jul 14 '11 at 21:09
  • The [API](http://download.oracle.com/javase/1.5.0/docs/api/java/lang/NullPointerException.html) says: "Thrown when an application attempts to use null in a case where an object is required. These include: [...] Applications should throw instances of this class to indicate other illegal uses of the null object." In fact, I have seen many APIs that throw a NullPointerException when being passed null. – fredoverflow Jul 15 '11 at 06:12
  • @Fred: Give me this weekend to research. I am afraid the only answer you may get is *because*. –  Jul 15 '11 at 15:22
  • @FredOverflow: Methods that act upon an *object* should throw NPE if given a null reference, while those which act upon *references* should recognize that a null reference *is* a reference, though it does not identify an object. The `equals` asks "Does this reference identify an object which is equivalent to you". If the reference identifies an object that could be equivalent, it may have to examine the details of the object to determine if it is equivalent, but if the reference doesn't identify an object of the proper type there's no reason for the code to examine the object itself at all. – supercat Oct 15 '13 at 18:08
0

I would go for

someObject.equals(null);

What is the rational for not having the second form throw a NullPointerException?

In this case, it won't throw NullPointerException.

Here we are sure that the object on which equals() is going to invoke is NOT NULL.

Saurabh Gokhale
  • 53,625
  • 36
  • 139
  • 164
0

The second bit doesn't throw an NPE because you are not dereferencing a null pointer. That code returns false because you are comparing a value to a non-value.

equals(null) will always return false because there is no such thing as a null value. Neither Object nor primitive can have the value null since the concept doesn't exist in Java. null is a literal that represents the null reference which is why we compare references, such as if (obj == null). See the Java language spec, section 3.10.7. In other words, you are comparing the value of someObject to the null reference.

You could make your own object, override equals, and return true but that would go against the definition in Object.

Paul
  • 19,704
  • 14
  • 78
  • 96