6

In the Standard Java API, are there any scenarios where == will return true, but equals will return false. While theoretically this could be written into a user-defined class rather trivially like this

class A {
    public boolean equals(Object o) {
        return this != o;
    }
}

Are there any actually baked in examples where for some objects b and c, b == c will return true, but b.equals(c) returns false? Additionally, would there be any possible benefit to have such a behavior?

Eli Sadoff
  • 7,173
  • 6
  • 33
  • 61
  • 1
    Yes, most of them. `==` and `equals()` work totally differently. Normally you want `equals()`. – markspace Jan 19 '17 at 18:39
  • @markspace Not where `==` is false but `equals` is `true`. – Eli Sadoff Jan 19 '17 at 18:40
  • @markspace I know they work differently. `==` is reference checking whereas `equals` is a class method. – Eli Sadoff Jan 19 '17 at 18:40
  • 2
    There may not be any *useful* situations where == is true but equals() is false, but consider if `equals()` is implemented to just always return `false`. They are two different things and you shouldn't count on them being related. – markspace Jan 19 '17 at 18:41
  • @markspace My question was if the behavior existed in the Java API and why it would be useful. – Eli Sadoff Jan 19 '17 at 18:42
  • 6
    Well it would already be violating the stated contract of `equals`... – Jon Skeet Jan 19 '17 at 18:42
  • @JonSkeet I see your and the OP's point, but I still feel like trying to conflate `==` and `equals()` is a mistake. Use the one with the proper semantics you need. Java has a big API and I'd be hard pressed to swear that there's no situations where `equals()` is going to unexpectedly return `false`. (What about comparing two `Floats` equals to `NAN`? Shouldn't that return `false`?) – markspace Jan 19 '17 at 18:46
  • 1
    @JonSkeet `Object#equals` is not documented to explicitly have if `==` is `true` then `equals` is `true`. The five things that it must fulfill are: _reflexive_, _symmetric_, _transitive_, _consistent_, and _null/non-null_. – Eli Sadoff Jan 19 '17 at 18:46
  • 2
    @EliSadoff: The reflexive requirement is exactly that: "for any non-null reference value x, x.equals(x) should return true." - and for any reference x, x == x is true. – Jon Skeet Jan 19 '17 at 18:53
  • @JonSkeet I realized that. Afforess' answer covers it nicely. I did not think about how `x.equals(x)` having to be true would mean that until Afforess made it more explicit. – Eli Sadoff Jan 19 '17 at 18:54

4 Answers4

8

No*.

The contract for equals has 5 rules, and the first one covers this case:

The equals method implements an equivalence relation on non-null object references:

  • It is reflexive: for any non-null reference value x, x.equals(x) should return true.
  • It is symmetric: for any non-null reference values x and y, x.equals(y) should return true if and only if y.equals(x) returns true.
  • It is transitive: for any non-null reference values x, y, and z, if x.equals(y) returns true and y.equals(z) returns true, then x.equals(z) should return true.
  • It is consistent: for any non-null reference values x and y, multiple invocations of x.equals(y) consistently return true or consistently return false, provided no information used in equals comparisons on the objects is modified.
  • For any non-null reference value x, x.equals(null) should return false.

Any object in the Java standard library that violates reflexivity would be a bug, and if you do discover such an object in the API, report it to Oracle.

*Less can be said for third-party libraries. Developers make mistakes or are ignorant of the equals contract. Generally this also qualifies as a bug in a third-party library, but YMMV.

Afforess
  • 894
  • 7
  • 15
  • Ah! I did not think about the reflexive nature. That works! – Eli Sadoff Jan 19 '17 at 18:49
  • 1
    This answer is not correct. There *are* classes in the JDK that don't honor that contract, without its being considered a bug. See e.g. https://docs.oracle.com/javase/7/docs/api/java/sql/Timestamp.html#equals(java.lang.Object) . – ruakh Jan 19 '17 at 18:54
  • @ruakh That's interesting. I feel that if the JDK sets out a contract, then all of its classes should follow that contract, but apparently they do not! Good find! – Eli Sadoff Jan 19 '17 at 18:55
  • @ruakh I don't see anything in the timestamp equals method documentation indicating it was not reflexive. Can you give an example of a case where it isn't? – Afforess Jan 19 '17 at 18:57
  • @Afforess: I believe that Timestamp.equals is indeed reflexive; but this answer makes many more claims than just reflexivity. – ruakh Jan 19 '17 at 19:05
  • @ruakh ah, I did not mean to claim violating the other `equals` rules were bugs. I tweaked my answer to clarify. – Afforess Jan 19 '17 at 19:12
  • @Afforess: OK, but how did you determine that "Any object in the Java standard library that violates reflexivity would be a bug"? Your only source seems to be the contract for equals, but as I pointed out, there are JDK classes that knowingly violate that contract. What makes reflexivity special? – ruakh Jan 19 '17 at 19:30
  • @ruakh These `equals` implementations would then be horribly broken. The method name makes it quite clear what it does and if it can't find out that the an object share common properties with itself, then it can't do that (reliably) with different objects. If this is on purpuse, then the method shouldn't be named "equals". – Tom Jan 19 '17 at 21:30
  • @ruakh "equals" is supposed to identify two objects as 'equal' if they share certain equal properties. This is the uasual contract and this is what the method name tells us. So checking if object A has the same properties as A (so itself) need to return true, or the method is broken. It can't hold up the contract or the meaning of the method name. – Tom Jan 19 '17 at 22:14
  • @Tom: Sorry, I still don't get your point. Everything that you say is true, but I don't understand why you're saying it, or why you're sending it `@ruakh`. What am I missing? – ruakh Jan 19 '17 at 22:31
  • @ruakh I'm writing that to you due to the last comment of you before mine, where you ask Afforess about missing reflexivity breaking the "equals" contract and being considered a bug. My argument is that reflexivity is the most basic ability of a method named "equals". If that method can't do that, then the method name might be bad. – Tom Jan 19 '17 at 22:46
  • @Tom: I'm not asking for an argument, I'm asking for a source. Right now Afforess's answer contains a huge source quote that **does not justify the claim**. The quote might *seem* to justify the claim, but I've already demonstrated that the quote cannot be interpreted that way (or, if it does, then it's wrong). If you think the right answer is "There's no explicit source, but it's just obvious", then please write your *own* answer saying that. – ruakh Jan 19 '17 at 22:56
  • @Tom: (I'd be fine with it, by the way, if the answer made clear that the quotation doesn't really justify the claim, and is just included for color. But instead, the answer is trying to give the illusion of being better-supported than it actually is.) – ruakh Jan 19 '17 at 22:59
1

In the Standard Java API, are there any scenarios where == will return true, but equals will return false[?]

Not as far as I am aware, and I am confident that any examples you discovered would be considered bugs.

In particular, if x and y are references such that x == y, then it must be the case that x.equals(y) evaluates to the same result as x.equals(x). The contract for Object.equals() (in its docs) says this, in part:

The equals method implements an equivalence relation on non-null object references:

  • It is reflexive: for any non-null reference value x, x.equals(x) should return true.

Thus any override of Object.equals() is semantically incorrect if it produces, for any references x and y, the result that x == y && !x.equals(y) is true.

John Bollinger
  • 160,171
  • 8
  • 81
  • 157
1

In the Standard Java API, are there any scenarios where == will return true, but equals will return false.

I'm not sure if this is exactly what you have in mind, but implementations of equals are not required to be threadsafe, and are not required to explicitly check if the argument is the same instance as this. So it's quite possible, in principle, for foo.equals(foo) to return false if foo is simultaneously being modified in another thread.

I doubt any JDK class is explicitly documented as not including this check; rather, that's considered an implementation detail, except for classes where that is the only check. But I've managed to get sb.equals(sb) to at least raise an ArrayIndexOutOfBoundsException when sb is a StringBuilder to which another thread is busily adding elements; so if you're particularly unlucky in your timing, it should also be capable of returning false.

Additionally, would there be any possible benefit to have such a behavior?

I really don't think so. The whole purpose of equals is to support things like Set and Map and Assert.assertEquals and so on. There are plenty of use-cases that don't use equals at all, but I can't imagine a non-terrible piece of code that does use equals but wants it to not denote a form of equality that's satisfied by identity.

That said, it's certainly possible for a non-terrible piece of code to have a bug that accidentally causes this. For example, I mentioned in a comment above that java.util.Date and java.sql.Timestamp have a design mistake (now officially codified) whereby date.equals(ts) can be true but ts.equals(date) is false. Someone trying to address this sort of issue might modify java.util.Date to include a check if (that.getClass() == Date.class); but then this would result in a non-reflexive equals implementation in any subclass that didn't explicitly override the parent implementation. (Of course, I wouldn't expect such a mistake in the JDK.)

Writing equals correctly in the face of inheritance is actually rather tricky, but fortunately there's a known solution that addresses all the intricacies in a simple way: http://www.artima.com/lejava/articles/equality.html.

ruakh
  • 175,680
  • 26
  • 273
  • 307
0

As you pointed out, it is certainly possible to implement equals such that x == x but !x.equals(x). However, doing so would violate the documentated behavior of equals, which states that this property must hold for any valid implementation. So you won't find any examples in the Java standard API that do this (unless there's a bug somewhere), and you'll find plenty of code that relies implicitly or explicitly on equals not behaving this way.

jacobm
  • 13,790
  • 1
  • 25
  • 27