Ignoring the equals() and hashcode() methods while performing object comparison

Consider the following Apex class:

global class DealInfo {     
    public Integer amount {get;set;} 
    public String amountCurrency {get;set;}  
} 

Now, we are in a situation where we need to sort a list of objects from the preceding class. Let's run the following anonymous code:

DealInfo o = new DealInfo(); 
o.amount = 200; 
o.amountCurrency = 'USD'; 
 
DealInfo o1 = new DealInfo(); 
o1.amount = 100; 
o1.amountCurrency = 'USD';  
List<DealInfo> lstDeals = new List<DealInfo>{o,o1}; 
lstDeals.sort(); 

If you are expecting the list to get sorted, then it's not so. We get the following error from Apex:

System.ListException: One or more of the items in this list is not Comparable. 

Salesforce already supports sorting primitive datatypes. However, to support the sorting capabilities for objects, such as wrapper classes or lists, we need to implement the Comparable interface provided by Apex. The Comparable interface forces the class to implement the following method:

global Integer compareTo(Object compareTo) { 
    // Your comparison logic here 
} 

The compareTo method returns the integer value using the following rules:

  •  0 means that both the records are the same
  • >0 means that the current record is greater than the record to be compared
  • <0 means that the current record is smaller than the record to be compared

Note

As per the best practices, the value 0 should be returned only when two objects are exactly equal.

So, a developer came up with the following code:

global class DealInfo implements Comparable  { 
     
    public Integer amount {get;set;} 
    public String amountCurrency {get;set;} 
     
    //Implement "compareTo" method from Comparable interface 
    global Integer compareTo(Object objToCompare) { 
        DealInfo obj = (DealInfo) objToCompare ;   
     
        //compare two objects       
        if(this == obj){ 
            return 0; 
        } 
        return this.amount - obj.amount; 
    }  
} 

The preceding code is not correct because to compare two objects, the == operator is used in the code snippet. This operator only compares the reference (address) of the objects and does not consider their actual content. So, we should use the equals method to compare objects in Apex.

After this discussion, an implementation of the compareTo method should look something like the following code snippet:

global class DealInfo implements Comparable  {     
    public integer amount {get;set;} 
    public String amountCurrency {get;set;} 
     
    //Implement "compareTo" method from Comparable interface 
    global Integer compareTo(Object objToCompare) { 
        DealInfo obj = (DealInfo) objToCompare ; 
 
        //Check if both objects are equal 
        if(this.equals(obj)){ 
            return 0; 
        } 
 
        //Which object is greater than other 
        return this.amount - obj.amount; 
    } 
} 

There may be many developers out there who knowingly or unknowingly ignore the importance of the equals() and hashcode() methods while implementing the Comparable interface.

In the preceding code, we should use the equals() method. The preceding code is not yet correct.

To understand the problem in detail, execute the following anonymous code:

DealInfo o = new DealInfo(); 
o.amount = 200; 
o.amountCurrency = 'USD'; 
 
DealInfo o1 = new DealInfo(); 
o1.amount = 200; 
o1.amountCurrency = 'USD'; 
 
System.assertEquals(o, o1); 

In the preceding code, when we try to check whether both the objects are equal using the System.assertEquals() method, we will receive an error saying that the objects are not the same as expected:

Line: 9, Column: 1 System.AssertException: Assertion Failed: Expected: DealInfo:[amount=200, amountCurrency=USD], Actual: DealInfo:[amount=200, amountCurrency=USD] 

Both the records are exactly the same but why does the assertion still fail? This is because Apex doesn't know how to compare the two objects, and it's the developer's job to implement the equals method to instruct Salesforce to determine equality between two objects. Along with equals(), we should implement the hashcode() method as well. Hashcodes are used by the platform to index the object location in heap memory. Another reason why we should implement hashcode() is that Salesforce supports Apex classes as keys in maps and sets. If we do not implement hashcode(), then the same object would not be retrieved if it's used as a key in a map or a set.

As a best practice, the following guidelines should be followed while overriding the equals() method:

  1. Check whether the record to be compared has the same location in memory. For this, we can use the exact equality operator ( = = = ) available in Apex.
  2. Check whether a record to be compared is null.
  3. Check whether both the records are of the same type to avoid typecasting an error.
  4. Use the actual logic to see whether the content of the two objects is the same or not.

Note

All the available operators in Apex are listed in the official documentation page at  https://developer.salesforce.com/docs/atlas.en-us.apexcode.meta/apexcode/langCon_apex_expressions_operators_understanding.htm.

General contracts between the and equals()hashcode() and equals() method in Apex are as follows:

  • If two objects are equal, then their hashcode must also be the same
  • If the hashcode is invoked multiple times on the same object in the same transaction, then the value should not be changed if none of the fields have been modified

Considering all the points that we discussed, the following code snippet is the correct implementation of the equals(), hashcode(), and compareTo() methods:

 global class DealInfo implements Comparable  { 
     
    public Integer amount {get;set;} 
    public String amountCurrency {get;set;} 
     
    //Implement "compareTo" method from Comparable interface 
    global Integer compareTo(Object objToCompare) { 
        DealInfo obj = (DealInfo) objToCompare ; 
        if(this.equals(obj)){ 
            return 0; 
        } 
        return this.amount - obj.amount; 
    } 
     
    //Override equals() method     
    public Boolean equals(Object o) { 
        //Rule 1 
        if (this === o) { 
           return true; 
        }  
        //Rule 2 
        if ( (o == null) || !(o instanceof DealInfo) ) { 
            return false; 
        }  
        //Rule 3 
        DealInfo obj = (DealInfo)o; 
        if(obj.amount == this.amount && obj.amountCurrency == this.amountCurrency) 
        { 
            return true; 
        } 
        return false; 
    } 
     
    //Override hashcode() method 
    public Integer hashCode() { 
        return this.amount  + this.amountCurrency.hashcode(); 
    } 
 
}  
..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset