| Reference types that declare an `equals()` method, or that inherit `equals()` |
| from a type other than `Object`, should not be compared for reference equality |
| with `==` or `!=`. Instead, always compare for value equality with `.equals()`. |
| |
| ## FAQs |
| |
| ### How about comparing interned objects? |
| |
| It's dangerous to rely on your instances being interned. We have no tooling to |
| check or enforce that, and it's easy to get wrong. |
| |
| ### But what about `Boolean` values? We _know_ there's just `TRUE` and `FALSE` (and `null`). Surely _they're_ okay! |
| |
| Well, no, because some tricky client can always generate a new instance with |
| `new Boolean(true)`. Comparing with `equals` always works; comparing with `==` |
| doesn't. |
| |
| ### How about a reference equality comparison before a more expensive content equality comparison? |
| |
| The check allows implementations of `Object#equals()` to perform reference |
| equality tests on the type equality is being implemented for. For example: |
| |
| ``` |
| abstract class Foo { |
| |
| abstract String bar(); |
| |
| @Override |
| public boolean equals(Object other) { |
| if (this == other) { |
| return true; // fast path, reference equality is allowed here |
| } |
| if (!(other instanceof Foo)) { |
| return false; |
| } |
| Foo that = (Foo) other; |
| // value equality should still be used for types other than `Foo` |
| return bar().equals(that.bar()); |
| } |
| } |
| ``` |
| |
| In other cases, calling `Type#equals()` should be just as fast, because that |
| method will likely be inlined, and the first thing it will likely do is that |
| same instance comparison. |
| |
| Alternatively, if you're okay with accepting `null`, you could call |
| `java.util.Objects.equals()`, which first does a reference equality comparison |
| and then falls back to content equality for non-null arguments. |
| |
| ### How about asserting in a test that two different references point to the same object (or not)? |
| |
| Both Truth and JUnit provide clearer ways to assert this. |
| |
| Truth: |
| |
| ``` |
| assertThat(a).isSameInstanceAs(b); |
| assertThat(a).isNotSameInstanceAs(b); |
| ``` |
| |
| JUnit: |
| |
| ``` |
| assertSame(b, a); |
| assertNotSame(b, a); |
| ``` |
| |
| ### How about comparing against a special marker instance? |
| |
| Classes override `equals` to express when two instances should be treated as |
| interchangeable with each other. Predominant Java libraries and practices are |
| built on that assumption. Defining a "magic instance" for such a type goes |
| against this whole practice, leaving you vulnerable to unexpected bugs. |
| |
| Consider choosing a sentinel value within the domain of the type (the moral |
| equivalent of `-1` for indexOf function calls) that you could compare against |
| using the normal `equals` method. |
| |
| ### So how can I put a special "nothing" value in my map? |
| |
| Use `Optional<V>` as the value type of your map instead. |