Saturday, December 27, 2014

Three Common Methods Generated in Three Java IDEs

In this post, I look at the differences in three "common" methods [equals(Object), hashCode(), and toString()] as generated by NetBeans 8.0.2, IntelliJ IDEA 14.0.2, and Eclipse Luna 4.4.1. The objective is not to determine which is best, but to show different approaches one can use for implementing these common methods. Along the way, some interesting insights can be picked up regarding creating of these common methods based on what the IDEs assume and prompt the developer to set.

NetBeans 8.0.2

NetBeans 8.0.2 allows the Project Properties to be configured to support the JDK 8 platform and to expect JDK 8 source formatting as shown in the next two screen snapshots.

Code is generated in NetBeans 8.0.2 by clicking on Source | Insert Code (or keystrokes Alt+Insert).

When generating the methods equals(Object), hashCode(), and toString(), NetBeans 8.0.2 asks for the attributes to be used in each of these generated methods as depicted in the next two screen snapshots.

The NetBeans-generated methods take advantage of the JDK 7-introduced Objects class.

NetBeans-Generated hashCode() Method for Class NetBeans802GeneratedCommonMethods.java
@Override
public int hashCode()
{
   int hash = 5;
   hash = 29 * hash + Objects.hashCode(this.someString);
   hash = 29 * hash + Objects.hashCode(this.timeUnit);
   hash = 29 * hash + this.integer;
   hash = 29 * hash + Objects.hashCode(this.longValue);
   return hash;
}
NetBeans-Generated equals(Object) Method for Class NetBeans802GeneratedCommonMethods.java
@Override
public boolean equals(Object obj)
{
   if (obj == null)
   {
      return false;
   }
   if (getClass() != obj.getClass())
   {
      return false;
   }
   final NetBeans802GeneratedCommonMethods other = (NetBeans802GeneratedCommonMethods) obj;
   if (!Objects.equals(this.someString, other.someString))
   {
      return false;
   }
   if (this.timeUnit != other.timeUnit)
   {
      return false;
   }
   if (this.integer != other.integer)
   {
      return false;
   }
   if (!Objects.equals(this.longValue, other.longValue))
   {
      return false;
   }
   return true;
}
NetBeans-Generated toString() Method for Class NetBeans802GeneratedCommonMethods.java
@Override
public String toString()
{
   return "NetBeans802GeneratedCommonMethods{" + "someString=" + someString + ", timeUnit=" + timeUnit + ", integer=" + integer + ", longValue=" + longValue + '}';
}

Some observations can be made regarding the NetBeans-generated common methods:

  • All generated code is automatic and does not support customization with the exception of the fields used in the methods which the operator selects.
  • All of these common methods that extend counterparts in the Object class automatically have the @Override annotation provided.
  • No Javadoc documentation is included for generated methods.
  • The methods make use of the Objects class to make the generated code more concise with less need for null checks.
  • Only one format is supported for the String generated by toString() and that output format is a single comma-delimited line.
  • I did not show it in the above example, but NetBeans 8.0.2's methods generation does treat arrays differently than references, enums, and primitives in some cases:
    • The generated toString() method treats array attributes of the instance like it treats other instance attributes: it relies on the array's toString(), which leads to often undesirable and typically useless results (the array's system identity hash code). It'd generally be preferable to have the string contents of array attributes provided by Arrays.toString(Object[]) or equivalent overloaded version or Arrays.deepToString(Object[]).
    • The generated hashCode() method uses Arrays.deepHashCode(Object[]) for handling arrays' hash codes.
    • The generated equals(Object) method uses Arrays.deepEquals(Object[], Object[]) for handling arrays' equality checks.
    • It is worth highlighting here that NetBeans uses the "deep" versions of the Arrays methods for comparing arrays for equality and computing arrays' hash codes while IntelliJ IDEA and Eclipse use the regular (not deep) versions of Arrays methods for comparing arrays for equality and computing arrays' hash codes.

 

IntelliJ IDEA 14.0.2

For these examples, I'm using IntelliJ IDEA 14.0.2 Community Edition.

IntelliJ IDEA 14.0.2 provides the ability to configure the Project Structure to expect a "Language Level" of JDK 8.

To generate code in IntelliJ IDEA 14.0.2, one uses the Code | Generate options (or keystrokes Alt+Insert like NetBeans).

IntelliJ IDEA 14.0.2 prompts the operator for which attributes should be included in the generated methods. It also asks which fields are non-null, meaning which fields are assumed to never be null. In the snapshot shown here, they are checked, which would lead to methods not checking those attributes for null before trying to access them. In the code that I generate with IntelliJ IDEA for this post, however, I won't have those checked, meaning that IntelliJ IDEA will check for null before accessing them in the generated methods.

IntelliJ IDEA 14.0.2's toString() generation provides a lengthy list of formats (templates) for the generated toString() method.

IntelliJ IDEA 14.0.2 also allows the operator to select the attributes to be included in the generated toString() method (selected when highlighted background is blue).

IDEA-Generated equals(Object) Method for Class Idea1402GeneratedCommonMethods.java
public boolean equals(Object o)
{
   if (this == o) return true;
   if (o == null || getClass() != o.getClass()) return false;

   Idea1402GeneratedCommonMethods that = (Idea1402GeneratedCommonMethods) o;

   if (integer != that.integer) return false;
   if (longValue != null ? !longValue.equals(that.longValue) : that.longValue != null) return false;
   if (someString != null ? !someString.equals(that.someString) : that.someString != null) return false;
   if (timeUnit != that.timeUnit) return false;

   return true;
}
IDEA-Generated hashCode() Method for Class Idea1402GeneratedCommonMethods.java
@Override
public int hashCode()
{
   int result = someString != null ? someString.hashCode() : 0;
   result = 31 * result + (timeUnit != null ? timeUnit.hashCode() : 0);
   result = 31 * result + integer;
   result = 31 * result + (longValue != null ? longValue.hashCode() : 0);
   return result;
}
IDEA-Generated toString() Method for Class Idea1402GeneratedCommonMethods.java
@Override
public String toString()
{
   return "Idea1402GeneratedCommonMethods{" +
      "someString='" + someString + '\'' +
      ", timeUnit=" + timeUnit +
      ", integer=" + integer +
      ", longValue=" + longValue +
      '}';
}

Some observations can be made regarding the IntelliJ IDEA-generated common methods:

  • Most generated code is automatic with minor available customization including the fields used in the methods which the operator selects, specification of which fields are expected to be non-null (so that null checks are not needed in generated code), and the ability to select one of eight built-in toString() formats.
  • All of these common methods that extend counterparts in the Object class automatically have the @Override annotation provided.
  • No Javadoc documentation is included for generated methods.
  • The generated methods do not make use of the Objects class and so require explicit checks for null for all references that could be null.
  • It's not shown in the above example, but IntelliJ IDEA 14.0.2 does treat arrays differently in the generation of these three common methods:

 

Eclipse Luna 4.4.1

Eclipse Luna 4.4.1 allows the Java Compiler in Project Properties to be set to JDK 8.

In Eclipse Luna, the developer uses the "Source" drop-down to select the specific type of source code generation to be performed.

Eclipse Luna allows the operator to select the attributes to be included in the common methods. It also allows the operator to specify a few characteristics of the generated methods. For example, the operator can choose to have the elements of an array printed individually in the generated toString() method rather than an often meaningless class name and system identity hash code presented.

Eclipse-Generated hashCode() Method for Class Eclipse441GeneratedCommonMethods.java
/* (non-Javadoc)
 * @see java.lang.Object#hashCode()
 */
@Override
public int hashCode()
{
   final int prime = 31;
   int result = 1;
   result = prime * result + this.integer;
   result = prime * result
         + ((this.longValue == null) ? 0 : this.longValue.hashCode());
   result = prime * result
         + ((this.someString == null) ? 0 : this.someString.hashCode());
   result = prime * result
         + ((this.timeUnit == null) ? 0 : this.timeUnit.hashCode());    return result;
}
Eclipse-Generated equals(Object) Method for Class Eclipse441GeneratedCommonMethods.java
/* (non-Javadoc)
 * @see java.lang.Object#equals(java.lang.Object)
 */
@Override
public boolean equals(Object obj)
{
   if (this == obj)
      return true;
   if (obj == null)
      return false;
   if (getClass() != obj.getClass())
      return false;
   Eclipse441GeneratedCommonMethods other = (Eclipse441GeneratedCommonMethods) obj;
   if (this.integer != other.integer)
      return false;
   if (this.longValue == null)
   {
      if (other.longValue != null)
         return false;
   } else if (!this.longValue.equals(other.longValue))
     return false;
   if (this.someString == null)
   {
      if (other.someString != null)
         return false;
   } else if (!this.someString.equals(other.someString))
      return false;
   if (this.timeUnit != other.timeUnit)
      return false;
   return true;
}
Eclipse-Generated toString() Method for Class Eclipse441GeneratedCommonMethods.java
/* (non-Javadoc)
 * @see java.lang.Object#toString()
 */
@Override
public String toString()
{
   return "Eclipse441GeneratedCommonMethods [someString=" + this.someString
         + ", timeUnit=" + this.timeUnit + ", integer=" + this.integer
         + ", longValue=" + this.longValue + "]";
}

Some observations can be made regarding the Eclipse-generated common methods:

  • Eclipse provides the most points in the generation process in which the generated output can be configured. Here are some of the configurable options:
    • Location in class (before or after existing methods of class) can be explicitly specified.
    • All of these common methods that extend counterparts in the Object class automatically have the @Override annotation provided.
    • "Method comments" can be generated, but they are not Javadoc style comments (use /* instead of /** and explicitly state they are not Javadoc comments as part of the generated comment).
    • Option to "list contents of arrays instead of using native toString()" allows developer to have Arrays.toString(Array) be used (same as IntelliJ IDEA's approach and occurs if checked) or have the system identify hash code be used (same as NetBeans's approach and occurs if not checked).
    • Support for four toString() styles plus ability to specify custom style.
    • Ability to limit the number of entries of an array, collection, or map that is printed in toString().
    • Ability to use instance of in generated equals(Object) implementation.
  • All of these common methods that extend counterparts in the Object class automatically have the @Override annotation provided.
  • The generated methods do not make use of the Objects class and so require explicit checks for null for all references that could be null.
  • Eclipse Luna 4.4.1 does treat arrays differently when generating the three common methods highlighted in this post:
    • Generated toString() optionally uses Arrays.toString(Object[]) or overloaded version for accessing contents of array.
    • Generated equals(Object) uses Arrays.equals(Object[], Object[]) or overloaded version for comparing arrays for equality.
    • Generated hashCode() uses Arrays.hashCode(Object[]) or overloaded version for computing hash code of array.
Conclusion

All three IDEs covered in this post (NetBeans, IntelliJ IDEA, and Eclipse) generate sound implementations of the common methods equals(Object), hashCode(), and toString(), but there are differences between the customizability of these generated methods across the three IDEs. The different customizations that are available and the different implementations that are generated can provide lessons for developers new to Java to learn about and consider when implementing these methods. While the most obvious and significant advantage of these IDEs' ability to generate these methods is the time savings associated with this automatic generation, other advantages of IDE generation of these methods include the ability to learn about implementing these methods and the greater likelihood of successful implementations without typos or other errors.