equals

Apex Custom Type Equals

Apex provides the ability for developers to define an equals method in their custom types.  The equals method, along with hashCode, should always be defined when custom types need to be used in Sets or as Map keys.  Additionally, the equality operator (==)  will use the equals implementation if it is defined.

Why

Of course, if you are writing code that needs to check equality of instances of your custom type or instances need to be used in Sets or as Map keys it makes sense to implement equals and hashCode.  However, if you are defining a custom type and are not immediately using it in a Set or as a Map key or in an equality comparison, it is still not a bad idea to implement the equals and hashCode, assuming there is a way to naturally compare instances for equality.

If you do not define an equals method the exact equality operator (===) will be used which checks to see if the two memory locations pointed to by the references are equal (equivalent of using == to compare references in Java).  When other developers use your custom type they may use the equals method, thinking it does the deeper comparison, when it does not (Bloch points this out in Effective Java). Sometimes errors like that aren’t caught immediately due to the type of test data used and end up making their way into production code.

Be Careful

If you do implement the equals method there are a few subtle things to consider.

The method signature is public Boolean equals(Object o).  That is, it takes an Object argument.  If you define it as public Boolean equals(CustomType o) it does not override the Object implementation and will not work with arguments other than that of your custom type.  Additionally, the == operator will not use the equals(CustomType o) implementation.

A common pattern for implementing equals which Java developers are familiar with is as follows:

  1. Check to see if the two references are the same and return true if they are.  This allows for a quick/efficient exit.
  2. If the argument is not an instance of the custom type, return false.  This allows for a quick/efficient exit and, more importantly, allows the argument to be safely cast to the custom type.
  3. Compare fields.

Coming from a Java background, you might be tempted to write the equal reference test (step #1) as follows:

The problem with this is that the == method is really just the equals method which is what is being defined. The above code will result in a stack overflow (maximum stack depth reached), because the == actually calls the same equals method. The correct way to implement the equal reference test is to use the exact equality operator (===).

Another gotcha is the instanceof test (step #2).  The equals method must return false when the argument passed in is null, according to its specification.  A common implementation of the instanceof check in equals is as follows:

This works in classes that are API version 32.0 and above because null instanceof anything always returns false as noted in the instanceof documentation.  But that is only for API versions 32.0 and above.

For classes that have an API version that is 31.0 or lower, null needs to be checked for explicitly, because a null in the left hand side operator of instanceof returns true in all cases for API versions 31.0 and below.  Note that in this case it is acceptable to use the == operator with null. It does not result in a stack overflow.

Don’t Forget Hashcode

If you implement equals you must implement hashCode or you will break the contract for hashCode. The specification for hashCode in the Apex documentation states that “If two objects are equal, based on the equals method, hashCode must return the same value.”

For example if you have a custom type MyClass and you override equals but don’t override hashCode you could have the following awkward situation where two objects are equal, but cannot be used interchangeably as Map keys.

More

There is good Salesforce documentation on equals and hashCode.  Additionally, if you want to implement equals or hashCode and are looking for examples to follow, Java examples and references are useful since the Salesforce docs state that the Apex implementations are based on the Java implementations.  One particular Java reference that is useful is Effective Java by Joshua Bloch.