Saturday, October 9, 2010

Seven Indispensable NetBeans Java Hints

I have found NetBeans Java Hints to be extremely useful in Java development. In this blog post I look at NetBeans Java Hints that I deem indispensable in Java development. I will be using NetBeans 6.9 for the screen snapshots in this post. As this Wiki page indicates, there are numerous hints new to NetBeans 6.9.

The NetBeans IDE makes it easier to control which hints are enabled. This granular level of control is desirable because not every hint is created equal. Some hints, in my opinion, should always be enabled and these are the subject of this post. Other hints, may not be quite as useful for general use and some might even get in the way. I like to have only the hints that matter enabled so that I have a better chance of clearing the yellow highlighting and margin marks that indicate a warning condition. I like to have a "clean" Java source file in NetBeans so that if any significant conditions are introduced, the emergence of yellow tips me off. It is nice to disable less important (to me) hints so that this clean IDE view of the source can be maintained.

It is easy to enable Java hints in NetBeans. Select "Tools" from the title bar, then select "Options" from the drop-down menu. Choose the "Editor" option and then select the "Hints" tab. At this point, make sure that the "Language" drop-down is set to "Java" (other choices include PHP, JavaScript, and Ruby). The screen snapshot below shows how this appears, including the twenty-four categories of hints. The + sign to the left of each hints category can be clicked on to expand it and see specific hints within each category. These can then be selected to have them enabled in the IDE.


One of the categories of hints is "Standard Javac warnings." These are the warnings that I discussed in my blog post javac -Xlint Options. As I stated in that post, some of these can bring a developer's attention to some important conditions in the Java code. In particular, the support for striking out deprecated code marked with @deprecated and @Deprecated is useful in keeping new code from using deprecated methods. The next screen snapshot shows these "Standard Javac warnings."


In this post, I won't look at any of NetBeans' hints in this "Standard Javac warnings" hints category because they are covered by javac's -Xlint. Instead, I focus in this post on warnings that NetBeans hints provide that are not available via javac. Indeed, NetBeans provides some handy warnings for some very dangerous ("suspicious") conditions that I wish the standard compiler warned me about!

It is arguable which is the "most important" of the NetBeans hints, but the hint for "suspicious method call" certainly should be high on anyone's list. This hint, which falls under "Probable Bugs" hints category, warns about the dangerous (more than suspicious in my mind) situation I described in the blog post The Contains Trap in Java Collections. The following code snippet shows code that causes this hint to lead to three warnings.

/**
    * This method demonstrates the NetBeans "Suspicious method call" hint.
    */
   private void demonstrateSuspiciousMethodsCalls()
   {
      final StringBuilder builder = new StringBuilder();
      builder.append("Hints");

      final Set<String> strings = new HashSet<String>();
      strings.add("NetBeans");
      strings.add("Hints");
      strings.add("http://marxsoftware.blogspot.com/");
      if (strings.contains(builder))
      {
         out.println("That String is contained!");
      }
      else
      {
         out.println("That String is NOT contained.");
      }

      final Map<String, String> stringsMap = new HashMap<String, String>();
      stringsMap.put("One", "NetBeans");
      stringsMap.put("Two", "Hints");
      stringsMap.put("Three", "http://marxsoftware.blogspot.com/");
      if (stringsMap.containsKey(builder))
      {
         stringsMap.remove(builder);
      }
   }

With the "suspicious method call" hint enabled as a warning, NetBeans clearly flags the three occurrences of this situation in the code above as demonstrated in the next image.


As the above screen snapshot indicates, NetBeans flags the three "suspicious method calls" that occur in the code. They are highlighted with yellow underlining and with yellow marks in the right margin. The light bulb icons on the left can be clicked on to see more details and potential resolution options.

In this case, I find this condition of significant enough concern that a warning is not sufficient. NetBeans allows me to specify that this hint results in an error instead of a warning. For example, I can click on the light bulb icon and select "Configure 'Suspicious method call' Hint" option to change it. The new screen is shown next.


With the "Show As" drop-down set to "Error," NetBeans now shows this significant issue as an error in the IDE! This is shown in the next screen snapshot, which is the same as above, but with error-level markings.


Setting the "suspicious method calls" hint to appear as error rather than warning helps make it even more obvious in large code bases. Although I won't specifically show this transition for each NetBeans hint to "error" in this post, it is not surprising that many of these "indispensable" hints could arguably be set to "error" rather than "warning" for NetBeans reporting. NetBeans gives us the flexibility to select which hints we want flagged and which hints we want flagged as errors versus as warnings. One important observation is that NetBeans's treatment of a hint as an error is only for viewing and does not prevent NetBeans from building the source code successfully.

An important NetBeans hint is the "Comparing Strings using == or != hint. This hint flags the well-known issue in Java when Strings (usually mistakenly) are compared using == or != instead of .equals. The danger is increased dramatically when coupled with String instantiations using the new keyword and that's what NetBeans's "String constructor" hint catches. The next code snippet leads to these two hints being triggered. The NetBeans response to that code is shown after the code.

/**
    * This method demonstrates two NetBeans hints: comparing strings with == or
    * != and string constructor initialization. These two suspicious cases are
    * especially problematic when they exist together as demonstrated in this
    * method.
    */
   private void demonstrateComparingStringsAndStringConstructorInitialization()
   {
      final String stringOne = new String("NetBeans");
      final String stringTwo = new String("NetBeans");
      if (stringOne == stringTwo)
      {
         out.println(stringOne + " equals " + stringTwo + ".");
      }
      else
      {
         out.println(stringOne + " does NOT equal " + stringTwo + ".");
      }
   }

Another dangerous problem occurs when a constructor calls a method on the same class that might be overridden by child method. This is demonstrated in the next code snippet, but fortunately NetBeans can identify this bad behavior with the "Overridable method call in constructor" hint.

package dustin.examples;

/**
 * This class is full of misbehavior to help demonstrate NetBeans hints.
 */
public class MisbehavingClass
{
   /**
    * This constructor does a bad thing: it calls an overridable method.
    */
   public MisbehavingClass()
   {
      initialize();   
   }

   protected void initialize()
   {
      // do some initialization
   }
}

Not surprisingly, the "Probable Bugs" category of NetBeans hints has numerous hints that warn of potentially serious complications. For example, the ".equals on incompatible types" hint will be triggered by the following code snippet.

/**
    * Demonstrate NetBeans ".equals on incompatible types" hint.
    */
   private void demonstrateIncompatibleEquals()
   {
      final String string = "String";
      final StringBuilder builder = new StringBuilder("String");
      if (string.equals(builder))
      {
         out.println(string + " equals " + builder);
      }
      else
      {
         out.println(string + " is NOT equal to " + builder);
      }
   }

This hint is in many ways like the "suspicious method call" hint covered earlier. Both of these hints warn about cases where the fact that the API necessarily needs to accept an Object instance leaves open the possibility of passing in types that simply can never match what is expected.

I always prefer to know about code issues as early as possible in the development cycle as possible. Specifically, when possible, I'd rather know about code that's flat-out wrong at compile time rather than at runtime. NetBeans includes several hints that let me know of a problem in my code that I'd otherwise not know about until runtime. I've already covered some of these above (the "suspicious method call" and ".equals on incompatible type" hints are two such examples), but the Probable Bugs category of hints provides another particularly useful hint in the "Incompatible Cast/Incompatible instanceof" hint.

This "Class is incompatible with instance of" hint notifies the developer that the type being checked in an outer instanceof operator check is not the same type (and is not compatible with) the type being cast when the instanceof evaluates to true. This is important because the compiler does not report an error or warning in this case. Instead, the problem is not encountered until runtime when a ClassCastException is thrown when the cast attempts to an incompatible type. A code snippet that will trigger this NetBeans hint is shown next.

/**
    * Demonstrate NetBeans hint "Cast is incompatible with given instanceof".
    */
   private void demonstrateCastIncompatibleWithInstanceOf()
   {
      final Object stringBuilder = new StringBuilder("string");
      if (stringBuilder instanceof String)
      {
         final StringBuilder newBuilder = (StringBuilder) stringBuilder;
         out.println("StringBuilder: " + newBuilder);
      }
      else if (stringBuilder instanceof StringBuilder)
      {
         final String newString = (String) stringBuilder;
         out.println("String: " + newString);
      }
   }

The above, when seen in NetBeans, looks like what is shown in the next screen snapshot.


The warning provided by NetBeans above about the cast type being incompatible with the type checked by the instanceof operator is much nicer (and much sooner) than when first encountered in runtime:


A third NetBeans hint from the Probable Bugs category is the "Incorrect Column Index in ResultSet" hint. Unlike many Java (and C/C++) based APIs, the columns in a result set accessed by column number are one-based rather than zero-based. A developer might accidentally (or out of habit) attempt to use a zero-based column index scheme. The next code sample shows how this might be done.

/**
    * Demonstrate NetBeans hint related to incorrect column index used with
    * JDBC {@code ResultSet}.
    */
   private void demonstrateIncorrectColumnIndexInResultSet()
   {
      final String jdbcUrl = "jdbc:oracle:thin:@localhost:1521:orcl";
      final String jdbcDriverName = "oracle.jdbc.pool.OracleDataSource";
      final String queryStr = "select employee_id, last_name, first_name from employees";
      try
      {
         Class.forName("oracle.jdbc.pool.OracleDataSource");
         final Connection connection = DriverManager.getConnection(jdbcUrl, "hr", "hr");
         final PreparedStatement statement = connection.prepareStatement(queryStr);
         final ResultSet rs = statement.executeQuery();
         while (rs.next())
         {
            final int id = rs.getInt(0);  // BAD FORM!!!
            final String lastName = rs.getString(1);
            final String firstName = rs.getString(2);
            out.println(firstName + " " + lastName + "'s ID is " + id + ".");
         }
      }
      catch (ClassNotFoundException cnfEx)
      {
         out.println(
              "Cannot find JDBC driver class '" + jdbcDriverName + "' - "
            + cnfEx.toString());
      }
      catch (SQLException sqlEx)
      {
         out.println("Don't do this: " + sqlEx.toString());
      }
   }

This NetBeans hint is also brought up for negative column indices, but that is less likely to happen then the use of a zero. This code will correctly compile, but will break at runtime with an SQLException ("Invalid column index"). Thankfully, NetBeans warns of this "probable bug" as shown in the next screen snapshot.



It is important to always provide an explicitly overridden hashCode() implementation when equals() is explicitly overridden for correct behavior. NetBeans provides a hint for this. The "Generate missing hashCode or equals" hint will warn if either of these two methods exists without the other. This is demonstrated in the following code sample and the image following the code sample shows NetBeans calling attention to the missing hashCode().

public boolean equals(Object obj)
   {
      if (obj == null)
      {
         return false;
      }
      if (this.getClass() != obj.getClass())
      {
         return false;
      }
      final MisbehavingClass other = (MisbehavingClass) obj;
      return this.variable != null ? this.variable.equals(other.variable) : other.variable == null;
   }


This image shows two NetBeans hints in action. The first is the "Generate missing hashCode()" hint because there is an equals(Object) method, but not a hashCode() method in this class. The second is another useful NetBeans hint that suggests that the developer should "Add @Override Annotation" to this method that overrides Object's hashCode(). I like to have this one turned on to ensure that I don't accidentally override a method that I am not intending to override. If I see this hint, I make sure it is supposed to be an overriding method and add the annotation or else change its name to not be overriding.

I've covered many aspects of NetBeans hints in this post. The ability to flag issues at compilation (or even before that) time rather than at runtime is very helpful. There are other tools that can do this, but it's really nice to have it incorporated and automatically implied directly in the IDE. Furthermore, the ability to select only those hints that are desired is useful and the ability to indicate whether a particular hint should be treated as a warning or error adds further control to the granularity of the hinting reporting. Finally, the ultimate flexibility is provided by the ability to create custom NetBeans hints, something I have not demonstrated in this post. With NetBeans 6.9 introducing so many useful hints, I'm finding reduced need of other static analysis tools.

NetBeans provides many useful hints that improve the quality, correctness, reliability, and maintainability of Java code. In this post, I've focused on some of them that I find particularly valuable. They are summarized here:
  1. Suspicious Method Call
  2. Comparing Strings Using == or !=  AND String Constructor
  3. Overridable Method Call in Constructor
  4. .equals Incompatible Types
  5. Incorrect Column Index in ResultSet
  6. Cast Incompatible with instanceof
  7. Generate .equals or .hashCode Method
These hints are so useful (almost always a real bug) and it is so advantageous to know about them sooner (compile/pre-compile time rather than runtime), that I'd like to see these be added to Sun's/Oracle's javac's -Xlint options.

1 comment:

Brad Cathey said...

Very nice post. I will be sure to look into this feature.