javac's -Xlint Options

The Java programming language compiler (javac) provided by Oracle (and formerly by Sun) has several non-standard options that are often useful. One of the most useful is the set of non-standard options that print out warnings encountered during compilation. That set of options is the subject of this post.

The javac page section on non-standard options lists and provides brief details on each of these options. The following is the relevant snippet from that page.

A listing of these options is also available from the command line (assuming the Java SDK is installed) with the command: javac -help -X. This is briefer than the man page/web page example shown above and is shown next.

As the previous snapshot from running javac -help -X indicates, the ten specific conditions for which Xlint warnings exist are (in alphabetical order): cast, deprecation, divzero, empty, fallthrough, finally, overrides, path, serial, and unchecked. I briefly look at each of these and provide a code snippet that leads to these warning occurring when Xlint is turned on. Note that the man page for javac and the Java SE 6 javac page both only list half of these Xlint options (documentation is apparently not as up-to-date as the javac usage/help). There is a useful NetBeans Wiki entry that summarizes all ten options.

The javac compiler allows all or none of the Xlint warnings to be enabled. If Xlint is not specified at all of the option -Xlint:none is explicitly specified, the behavior is to not show most of the warnings. Interestingly, the output does provide a warning about deprecation and unchecked warnings and recommends running javac with -Xlint enabled to see the details on these two types of warnings.

Before the end of this post, I'll demonstrate Java code that leads to 13 total reported Xlint warnings covering all ten of the options discussed above. However, without Xlint specified, the output is as shown in the next screen snapshot.

As the above image indicates, whether Xlint is not specified at all or is specified explicitly with "none", the result is the same: the majority of the warnings are not shown, but there are simple references to the deprecation and unchecked warnings with recommendations to run javac with -Xlint:deprecation and -Xlint:unchecked respectively for additional details. Running javac with -Xlint:all or -Xlint with no other options will show all warnings and would work to see the details regarding deprecated, unchecked, and all other applicable Xlint-enabled warnings. This will be shown after going through the source code and each Xlint warning individually.

-Xlint:cast

This option can be used to have the compiler warn the developer that a redundant cast is being made. Here is a code snippet that would get flagged if -Xlint, -Xlint:all, or -Xlint:cast was provided to javac when compiling the source.

/**
    * Demonstrates -Xlint:cast warning of a redundant cast.
    */
   private static void demonstrateCastWarning()
   {
      final Set<Person> people = new HashSet<Person>();
      people.add(fred);
      people.add(wilma);
      people.add(barney);
      for (final Person person : people)
      {
         // Redundant cast because generic type explicitly is Person
         out.println("Person: " + ((Person) person).getFullName());
      }
   }

In the above code, there is no need to cast the person object inside the for loop to Person and -Xlint:cast will warn of this unnecessary and redundant cast with a message stating something like:

src\dustin\examples\Main.java:37: warning: [cast] redundant cast to dustin.examples.Person
          out.println("Person: " + ((Person) person).getFullName());
                                    ^

-Xlint:deprecation

As discussed above, the Xlint deprecation warning was evidently deemed important enough to justify it being advertised even when Xlint is not explicitly run. This warning occurs when a deprecated method is invoked. The following code example demonstrates such a case.

/**
    * Cause -Xlint:deprecation to print warning about use of deprecated method.
    */
   private static void demonstrateDeprecationWarning()
   {
      out.println("Fred's full name is " + fred.getName());
   }

You cannot tell without the source code for the Person class (of which "fred" is an instance), but that getName() method is deprecated in Person. The following output from running javac with -Xlint, -Xlint:all, or -Xlint:deprecation confirms that (or points it out if the developer missed it).

src\dustin\examples\Main.java:47: warning: [deprecation] getName() in dustin.examples.Person has been deprecated
          out.println("Fred's full name is " + fred.getName());
                                                   ^

-Xlint:divzero

The divzero Xlint option indicates when integral division divides by a literal zero. A code example that will demonstrate this is shown next:

/**
    * Demonstrate -Xlint:divzero in action by dividing an int by a literal zero.
    */
   private static void demonstrateDivideByZeroWarning()
   {
      out.println("Two divided by zero is " + divideIntegerByZeroForLongQuotient(2));
   }

   /**
    * Divide the provided divisor into the provided dividend and return the
    * resulting quotient. No checks are made to ensure that divisor is not zero.
    *
    * @param dividend Integer to be divided.
    * @return Quotient of division of dividend by literal zero.
    */
   private static long divideIntegerByZeroForLongQuotient(final int dividend)
   {
      // Hard-coded divisor of zero will lead to warning. Had the divisor been
      // passed in as a parameter with a zero value, this would not lead to
      // that warning.
      return dividend / 0;
   }

The output from javac when the above is compiled is now shown.

src\dustin\examples\Main.java:231: warning: [divzero] division by zero
      return dividend / 0;
                        ^

When I intentionally tried to force this warning, it seemed to only work for a hard-coded (literal) zero divisor. Also, it does not flag double division because Infinity can be returned as a valid answer in that case without throwing an exception.

-Xlint:empty

The purpose of -Xlint:empty is to notify the developer that an "empty" if conditional is in the code. From my tests, this seems to only apply for the case of the empty "if" block. NetBeans provides "hints" (those yellow underlined warnings that are also marked in the right margin of the source code editor) for several types of empty statements, but -Xlint:empty seems to only flag the empty "if" statements. I included the others that NetBeans flags along with the one -Xlint:empty flags in the next source code sample.

/**
    * This method demonstrates how javac's -Xlint:empty works. Note that javac's
    * -Xlint:empty will only flag the empty statement involved in the "if" block,
    * but does not flag the empty statements associated with the do-while loop,
    * the while loop, the for loop, or the if-else. NetBeans does flag these if
    * the appropriate "Hints" are turned on.
    */
   private static void demonstrateEmptyWarning()
   {
      int[] integers = {1, 2, 3, 4, 5};
      if (integers.length != 5);
         out.println("Not five?");

      if (integers.length == 5)
         out.println("Five!");
      else;
         out.println("Not Five!");

      do;
      while (integers.length > 0);

      for (int integer : integers);
         out.println("Another integer found!");

      int counter = 0;
      while (counter < 5);

      out.println("Extra semicolons.");;;;
   }

The code above is filled with problematic placement of semicolons that almost certainly are not what the developer wanted. This code will compile, but the developer is warned of these suspicious situations if -Xlint, -Xlint:all, or -Xlint:empty is used with javac. The warning messages that are printed in the otherwise successful compilation are shown next.

src\dustin\examples\Main.java:197: warning: [empty] empty statement after if
      if (integers.length != 5);
                               ^

Only the empty "if" statement clause is flagged; the others are not reported by -Xlint:empty.

-Xlint:fallthrough

A tempting but controversial convenience Java provides is the ability to "fallthrough" common expressions in a switch statement to apply the same logic to multiple integral values with one piece of code. If all of the integral values with the shared functionality are empty except the final one that actually performs the functionality and provides a break, the -Xlint:fallthrough won't be activated. However, if some of the case expressions do perform their own logic in addition to the common fallthrough logic, this warning is produced. An examples that demonstrates this is shown next.

/**
    * Cause -Xlint:fallthrough to print warning about use of switch/case
    * fallthrough.
    */
   private static void demonstrateFallthroughWarning()
   {
      out.print("Wilma's favorite color is ");
      out.print(wilma.getFavoriteColor() + ", which is ");

      // check to see if 'artistic' primary color
      // NOTE: This one will not lead to -Xlint:fallthrough flagging a warning
      //       because no functionality is included in any of the case statements
      //       that don't have their own break.
      switch (wilma.getFavoriteColor())
      {
         case BLUE:
         case YELLOW:
         case RED:
            out.print("a primary color for artistic endeavors");
            break;
         case BLACK:
         case BROWN:
         case CORAL:
         case EGGSHELL:
         case GREEN:
         case MAUVE:
         case ORANGE:
         case PINK:
         case PURPLE:
         case TAN:
         case WHITE:
         default:
            out.print("NOT a primary artistic color");
      }
      out.print(" and is ");
      // check to see if 'additive' primary color
      // NOTE: This switch WILL lead to -Xlint:fallthrough emitting a warning
      //       because there is some functionality being performed in a case
      //       expression that does not have its own break statement.
      switch (wilma.getFavoriteColor())
      {
         case BLUE:
         case GREEN:
            out.println("(it's not easy being green!) ");
         case RED:
            out.println("a primary color for additive endeavors.");
            break;
         case BLACK:
         case BROWN:
         case CORAL:
         case EGGSHELL:
         case MAUVE:
         case ORANGE:
         case PINK:
         case PURPLE:
         case TAN:
         case YELLOW:
         case WHITE:
         default:
            out.println("NOT a primary additive color.");
      }
   }

The above code example intentionally shows both cases (pun intended) of the switch/case that will and will not lead to a warning message thanks to -Xlint:fallthrough. The output, with only one warning, is shown next.

src\dustin\examples\Main.java:95: warning: [fallthrough] possible fall-through into case
         case RED:
         ^

The case that got flagged was the RED case following the GREEN case that did some logic of its own before falling through to the RED logic.

-Xlint:finally

More than one person has warned, "Don't return in a finally clause." In fact, "Java's return doesn't always" is in The Java Hall of Shame. A Java developer can be warned about this nefarious situation by using -Xlint, -Xlint:all, or -Xlint:finally. A piece of source code demonstrating how this warning could be generated is shown next.

/**
    * Demonstrate -Xlint:finally generating warning message when a {@code finally}
    * block cannot end normally.
    */
   private static void demonstrateFinallyWarning()
   {
      try
      {
         final double quotient = divideIntegersForDoubleQuotient(10, 0);
         out.println("The quotient is " + quotient);
      }
      catch (RuntimeException uncheckedException)
      {
         out.println("Caught the exception: " + uncheckedException.toString());
      }
   }


   /**
    * Divide the provided divisor into the provided dividend and return the
    * resulting quotient. No checks are made to ensure that divisor is not zero.
    *
    * @param dividend Integer to be divided.
    * @param divisor Integer by which dividend will be divided.
    * @return Quotient of division of dividend by divisor.
    */
   private static double divideIntegersForDoubleQuotient(final int dividend, final int divisor)
   {
      double quotient = 0.0;
      try
      {
         if (divisor == 0)
         {
            throw new ArithmeticException(
               "Division by zero not allowed: cannot perform " + dividend + "/" + divisor);
         }
         // This would not have led to Xlint:divzero warning if we got here
         // with a literal zero divisor because Infinity would have simply been
         // returned rather than implicit throwing of ArithmeticException.
         quotient = (double) dividend / divisor;
      }
      finally
      {
         return quotient;
      }
   }

The above is flawed and likely isn't what the developer intended. The relevant warning javac provides when Xlint is enabled is shown next.

src\dustin\examples\Main.java:159: warning: [finally] finally clause cannot complete normally
      }
      ^

-Xlint:overrides

1 2 3 Page 1
Page 1 of 3
InfoWorld Technology of the Year Awards 2023. Now open for entries!