Consistency within the language. Having an operator that acts differently can be surprising to the programmer. Java doesn't allow users to overload operators - therefore reference equality is the only reasonable meaning for ==
between objects.
Within Java:
- Between numeric types,
==
compares numeric equality
- Between boolean types,
==
compares boolean equality
- Between reference types,
==
compares reference equality
- Use
.equals(Object o)
to compare values
That's it. Simple rule and simple to identify what you want. This is all covered in section 15.21 of the JLS. It comprises three subsections that are easy to understand, implement, and reason about.
Once you allow overloading of ==
, the exact behavior isn't something that you can look to the JLS and put your finger on a specific item and say "that's how it works," the code can become difficult to reason about. The exact behavior of ==
may be surprising to a user. Every time you see it, you have to go back and check to see what it actually means.
Since Java doesn't allow for overloading of operators, one needs a way to have a value equality test that you can override the base definition of. Thus, it was mandated by these design choices. ==
in Java tests numeric for numeric types, boolean equality for boolean types, and reference equality for everything else (which can override .equals(Object o)
to do whatever they want for value equality).
This is not an issue of "is there a use case for a particular consequence of this design decision" but rather "this is a design decision to facilitate these other things, this is a consequence of it."
String interning, is one such example of this. According to the JLS 3.10.5, all string literals are interned. Other strings are interned if one invokes .intern()
on them. That "foo" == "foo"
is true is a consequence of design decisions made to minimize the memory footprint taken up by String literals. Beyond that, String interning is something that is at the JVM level that has a little bit of exposure to the user, but in the overwhelming vast majority of cases, should not be something that concerns the programmer (and use cases for programmers wasn't something that was high on the list for the designers when considering this feature).
People will point out that +
and +=
are overloaded for String. However, that is neither here nor there. It remains the case that if ==
has a value equality meaning for String (and only String), one would need a different method (that only exists in String) for reference equality. Furthermore, this would needlessly complicate methods that take Object and expect ==
to behave one way and .equals()
to behave another requiring users to special case all those methods for String.
The addition of auto boxing/unboxing of primitive wrappers (e.g. java.lang.Integer, etc.) muddles things a bit because comparing an int
with an Integer
(or vice-versa) with ==
will indeed compare the integer values of each of those things, but if both variables are of type Integer
then using ==
may give an unexpected answer where the integer values are equal but the references are different, yielding a false
comparison.
The consistent contract for ==
on Objects is that it is reference equality only and that .equals(Object o)
exists for all objects which should test for value equality. Complicating this complicates far too many things.
==
is object equality andeq
is reference equality (ofps.oreilly.com/titles/9780596155957/…).==
operator only maps toEquals
if the==
operator was implemented that way. The default behavior for==
is the same asReferenceEquals
(actually,ReferenceEquals
is defined as the object version of==
)