A little note on referential equality in Java
Answering M. F. Hasler’s question, I casually tossed off the term “referential equality.” It’s one of those terms that I’ve come to take for granted, forgetting that sometimes it needs to be explained, even if the reader has a good idea what I meant.
Consider this Java unit testing toy example:
@Test
public void testAdditionToyExample() {
int expected = 2;
int actual = 1 + 1;
assert expected == actual;
}
Suppose expected
gets allocated in memory at 04F0 and actual
gets allocated right after at 04F4. The memory allocations don’t matter for the assertion: expected
and actual
hold the same value, so the test passes, just as one would expect.
Now consider this other unit testing toy example:
@Test
public void testStringToyExample() {
String phrase = "Hello, world!";
String expected = "hello, world!";
String actual = phrase.toLowerCase();
assert expected == actual;
}
Since both expected
and actual
hold “hello, world!”, we would expect the test to pass. But instead the assertion fails.
That’s because in Java, int
is a primitive, String
is a reference type, and the ==
operator works differently for reference types.
Let’s say expected
was allocated at 32A1 BEC0 and actual
at 2292 7A81. For a reference type like String
, the ==
operator checks the pointers. In this case, they don’t match, so the assertion fails.
The only way this second assertion can pass is if expected
and actual
point to the exact same object.
In Java, every class has equals()
, if nothing else, by inheritance from Object
. But equals()
just does a comparison with the ==
operator. By overriding equals()
, a class like String
can customize equals()
to do something more useful.