Core Java

Guava’s Objects Class: Equals, HashCode, and ToString

If you are fortunate enough to be using JDK 7, the newly available Objects class is the obvious (at least to me) choice for implementing the ‘common’ Java object methods such as equals(Object) [with Objects.equals(Object,Object)], hashCode() [with Objects.hashCode(Object) or Objects.hash(Object…)], and toString() [with Objects.toString(Object)] to appropriately override the default Object implementations. I have written posts about using Objects class: JDK 7: The New Objects Class and Java 7 Objects-Powered Compact Equals.

If you’re not yet using Java 7, your best choices might be the Apache Commons builders ToStringBuilder and EqualsBuilder and HashCodeBuilder (if you’re using a version of Java prior to J2SE 5) or Guava (if you’re using J2SE 5 or later). In this post, I look at using Guava‘s Objects class to implement the three common methods equals, hashCode, and toString().

Without Guava or other library to help, the three common methods discussed in this post are often highlighted as shown in the next code listing. These methods were generated with NetBeans 7.1 beta.

TraditionalEmployee
 

package dustin.examples;

import java.util.Calendar;

/**
 * Simple employee class using NetBeans-generated 'common' methods
 * implementations that are typical of many such implementations created
 * without Guava or other library.
 * 
 * @author Dustin
 */
public class TraditionalEmployee
{
   public enum Gender{ FEMALE, MALE };

   private final String lastName;
   private final String firstName;
   private final String employerName;
   private final Gender gender;

   /**
    * Create an instance of me.
    * 
    * @param newLastName The new last name my instance will have.
    * @param newFirstName The new first name my instance will have.
    * @param newEmployerName The employer name my instance will have.
    * @param newGender The gender of my instance.
    */
   public TraditionalEmployee(
      final String newLastName, final String newFirstName,
      final String newEmployerName, final Gender newGender)
   {
      this.lastName = newLastName;
      this.firstName = newFirstName;
      this.employerName = newEmployerName;
      this.gender = newGender;
   }

   public String getEmployerName()
   {
      return this.employerName;
   }

   public String getFirstName()
   {
      return this.firstName;
   }

   public Gender getGender()
   {
      return this.gender;
   }

   public String getLastName()
   {
      return this.lastName;
   }

   /**
    * NetBeans-generated method that compares provided object to me for equality.
    * 
    * @param obj Object to be compared to me for equality.
    * @return {@code true} if provided object is considered equal to me or
    *    {@code false} if provided object is not considered equal to me.
    */
   @Override
   public boolean equals(Object obj)
   {
      if (obj == null)
      {
         return false;
      }
      if (getClass() != obj.getClass())
      {
         return false;
      }
      final TraditionalEmployee other = (TraditionalEmployee) obj;
      if ((this.lastName == null) ? (other.lastName != null) : !this.lastName.equals(other.lastName))
      {
         return false;
      }
      if ((this.firstName == null) ? (other.firstName != null) : !this.firstName.equals(other.firstName))
      {
         return false;
      }
      if ((this.employerName == null) ? (other.employerName != null) : !this.employerName.equals(other.employerName))
      {
         return false;
      }
      if (this.gender != other.gender)
      {
         return false;
      }
      return true;
   }

   /**
    * NetBeans-generated method that provides hash code of this employee instance.
    * 
    * @return My hash code.
    */
   @Override
   public int hashCode()
   {
      int hash = 3;
      hash = 19 * hash + (this.lastName != null ? this.lastName.hashCode() : 0);
      hash = 19 * hash + (this.firstName != null ? this.firstName.hashCode() : 0);
      hash = 19 * hash + (this.employerName != null ? this.employerName.hashCode() : 0);
      hash = 19 * hash + (this.gender != null ? this.gender.hashCode() : 0);
      return hash;
   }

   /**
    * NetBeans-generated method that provides String representation of employee
    * instance.
    * 
    * @return My String representation.
    */
   @Override
   public String toString()
   {
      return  'TraditionalEmployee{' + 'lastName=' + lastName + ', firstName=' + firstName
            + ', employerName=' + employerName + ', gender=' + gender +  '}';
   }
}

Although NetBeans 7.1 beta did the heavy lifting here, this code still must be maintained and can be made more readable. The next class is the same class, but with Guava-powered common methods instead of the NetBeans-generated ‘typical’ implementations shown above.

GuavaEmployee
 

package dustin.examples;

/**
 * Simple employee class using Guava-powered 'common' methods implementations.
 * 
 * I explicitly scope the com.google.common.base.Objects class here to avoid the
 * inherent name collision with the java.util.Objects class.
 * 
 * @author Dustin
 */
public class GuavaEmployee
{
   public enum Gender{ FEMALE, MALE };

   private final String lastName;
   private final String firstName;
   private final String employerName;
   private final TraditionalEmployee.Gender gender;

   /**
    * Create an instance of me.
    * 
    * @param newLastName The new last name my instance will have.
    * @param newFirstName The new first name my instance will have.
    * @param newEmployerName The employer name my instance will have.
    * @param newGender The gender of my instance.
    */
   public GuavaEmployee(
      final String newLastName, final String newFirstName,
      final String newEmployerName, final TraditionalEmployee.Gender newGender)
   {
      this.lastName = newLastName;
      this.firstName = newFirstName;
      this.employerName = newEmployerName;
      this.gender = newGender;
   }

   public String getEmployerName()
   {
      return this.employerName;
   }

   public String getFirstName()
   {
      return this.firstName;
   }

   public TraditionalEmployee.Gender getGender()
   {
      return this.gender;
   }

   public String getLastName()
   {
      return this.lastName;
   }

   /**
    * Using Guava to compare provided object to me for equality.
    * 
    * @param obj Object to be compared to me for equality.
    * @return {@code true} if provided object is considered equal to me or
    *    {@code false} if provided object is not considered equal to me.
    */
   @Override
   public boolean equals(Object obj)
   {
      if (obj == null)
      {
         return false;
      }
      if (getClass() != obj.getClass())
      {
         return false;
      }
      final GuavaEmployee other = (GuavaEmployee) obj;

      return   com.google.common.base.Objects.equal(this.lastName, other.lastName)
            && com.google.common.base.Objects.equal(this.firstName, other.firstName)
            && com.google.common.base.Objects.equal(this.employerName, other.employerName)
            && com.google.common.base.Objects.equal(this.gender, other.gender);
   }

   /**
    * Uses Guava to assist in providing hash code of this employee instance.
    * 
    * @return My hash code.
    */
   @Override
   public int hashCode()
   {
      return com.google.common.base.Objects.hashCode(
                this.lastName, this.firstName, this.employerName, this.gender);
   }

   /**
    * Method using Guava to provide String representation of this employee
    * instance.
    * 
    * @return My String representation.
    */
   @Override
   public String toString()
   {
      return com.google.common.base.Objects.toStringHelper(this)
                .addValue(this.lastName)
                .addValue(this.firstName)
                .addValue(this.employerName)
                .addValue(this.gender)
                .toString();
   }
}

As the code above proves, the use of Guava improves the readability of the implementations of the three common methods. The only thing that’s not so nice is the need to explicitly scope Guava’s Objects class in the code to avoid a naming collision with Java SE 7’s Objects class. Of course, if one is not using Java 7, then this is not an issue and if one is using Java 7, it’s most likely that the standard version should be used instead anyway.

Conclusion

Guava provides a nice approach for building safer and more readable common methods via its Objects class. Although I’ll use the new java.util.Objects class instead for JDK 7 projects, Guava’s com.google.common.base.Objects class provides a nice alternative for working in versions of Java prior to JDK 7.
 

Reference: Guava’s Objects Class: Equals, HashCode, and ToString from our JCG partner Dustin Marx at the Inspired by Actual Events blog.

Subscribe
Notify of
guest

This site uses Akismet to reduce spam. Learn how your comment data is processed.

0 Comments
Inline Feedbacks
View all comments
Back to top button