JavaRanch Home    
 
This page:         last edited 24 September 2014         What's Changed?         Edit

Avoid The Equality Operator   

It's a rare day indeed that we don't get some question about '==' on these forums, especially as it relates to Strings.

Personally, I blame the SCJP/OCJP exams, which still, after all this time, contain questions designed to test your knowledge of Java's String pool; this despite the fact that once you've passed the exam, you will probably never need it again.

The first thing to know is that the SCJP exam is not about good programming; it's about understanding the Java compiler. In fact, most of the code examples written for the exams could be cited as examples of BAD programming, so my advice to you is to forget them the second you walk out of the exam room.

And the first thing to forget is using '==' to compare references.

There are three, and only three, reasons for using '==' in the Java language:

  1. For comparing primitives (and a String is NOT a primitive).
  2. For comparing a reference or expression with null.
  3. For seeing if two references or expressions refer to the exact same object, or both are equal to null ('null == null' evaluates to true).

In the first two cases, the language forces you to use '=='; but, apart from the exceptions listed at the end of this document (and there are only two), situations requiring option 3 are extremely rare. In 12 years of writing Java, I can still count the number of times I've needed a true identity comparison for regular objects on the fingers of both hands. [1]

So what should you use instead?

equals()...ALWAYS.

No "if"s, no "but"s, no "what about my particular tortured situation"s...ALWAYS.

Get out of the habit of writing '=='. Especially with Strings.

Why?

Because a correctly written equals() method will have an '==' check as its first line of code, that's why.

"But what about that String pool?", I hear you ask.

FORGET ABOUT IT.

I'd rather see a program littered with:

String myString = new String("some literal");
than a single instance of:
if (myString == someOtherString) { ...

The fact is that the first of those two statements should be:

String myString = "some literal";
but getting it wrong is far less likely to cause you any problems than using that second statement even once.

And the same is true of ALL Java wrapper classes (Integer, Long, Character, Double, Float, Byte, Short and Boolean)...indeed all Java objects.

DON'T USE '==' unless you're forced to.

By all means, read up about the String pool, or indeed the internal caches used by the other classes listed above, but don't use '=='.

GOT IT?



Exceptio probat regulam ("the exception confirms the rule")

equals()
One place that an identity comparison (case 3 above) does make sense is inside an equals() method. Why? Because an object must be equal to itself. It's part of the "equals contract" as documented in Object.equals() - and an '==' check is usually a lot faster than checking whether two references are 'logically equal'.

Indeed, most guides on how to write equals() methods properly say that you should make it the first check you do, viz:
	if (this == comparisonObject) return true;

Enums
Enum values are objects, but they are a very specific type of object: they are singletons.
That means that only one instance of a particular enum value can exist in the JVM; and that in turn means that a reference can only be "equal" to an enum value if it IS that value, so many programmers will simply use ‘==’ for clarity, rather than equals(), when dealing with Enums.
It should however be pointed out that it is not wrong to use equals() with enum values – just unusual – and it will work exactly as it should unless the reference it is called with is null (see below).

Nulls
This isn't so much an exception as a point to remember: equals() is an instance method and, as such, requires an object to work with. If you call it on a reference whose value is null, you will get a NullPointerException (NPE).

The problem is that sometimes it's difficult to know if a variable or parameter reference is null (it may have been set a long way away from the code being executed). One way around this when dealing with Enums or String literals is to use a trick called 'equals() inversion'. All this means is that you call the method with the reference that you know cannot be null.

Take, for example, the comparison:
	if (str.equals("Hello")) ...
It looks fine, but will throw a NPE if 'str' is null.
How do you get around it? Well, one possibility is:
	if (str != null && str.equals("Hello")) ...
but another, neater, solution is
	if ("Hello".equals(str)) ...
It looks odd at first, but is guaranteed to work because the reference for "Hello" cannot be null, so the expression can't possibly throw a NPE. If 'str' is null, it will return false.
The same also works for Enums. The expression:
	if (gender.equals(Gender.MALE)) ...
will throw an NPE if 'gender' is null, but
	if (Gender.MALE.equals(gender)) ...
can't, since Gender.MALE cannot possibly be null.
Of course
	if (gender == Gender.MALE) ...
works just as well, and it doesn’t matter which order you put the operands.

.
But we're trying to get you OUT of the habit of using '==', remember?''



[1]   It was pointed out to me by a colleague that there are valid occasions for using '==' with reference types (for example, for checking the origin of an event listener); but they tend to be more advanced, so I still say you should AVOID it until you know exactly when it's a reasonable thing to do. It's the exception, rather than the rule, and should always feel "odd" when you write it or see it.


CategoryWinston

JavaRanchContact us — Copyright © 1998-2014 Paul Wheaton