Showing posts with label javadoc. Show all posts
Showing posts with label javadoc. Show all posts

Tuesday, 15 February 2011

Javadoc all exceptions

All exceptions thrown by a method can be documented with the @throws javadoc comment (same as @exception). Some argue that an @throws should be included for every thrown exception, since it is the only way to clearly state all requirements made upon the caller (the pre-conditions). However, others disagree.

If all null object parameters passed to class methods cause a NullPointerException, then it is acceptable to state this once in the class javadoc comment, instead of repeating it for every method. Another alternative is to state in overview.html (javadoc's summary description of an application) that all parameters are to be considered as non-null unless explicitly stated otherwise.

Example

This example has an unusually large number of pre-conditions. Remarks regarding NullPointerException are placed in the class level javadoc comment.

Note that the unchecked SecurityException is thrown indirectly, through the calls to the methods of the File class.

import java.io.*;
/**
* If a null object parameter is passed to any method, then a
* <code>NullPointerException</code> will be thrown.
*/
public class WriteTextFile {
  //..other methods elided
  /**
  * Change the contents of a text file in its entirety, overwriting any
  * existing text.
  *
  * @param aFile is an existing file (not a directory) which can be written.
  * @param aContents is the text to be placed into aFile.
  *
  * @exception FileNotFoundException if aFile does not exist.
  * @exception IOException if stream to aFile cannot be written to or closed.
  *
  * @exception IllegalArgumentException if aFile is a directory.
  * @exception IllegalArgumentException if aFile cannot be written.
  * @exception SecurityException if a SecurityManager exists and
  * disallows read or write access to aFile.
  */
  static public void setContents(File aFile, String aContents)
                                 throws FileNotFoundException, IOException {
    if (aFile == null) {
      throw new NullPointerException("File must not be null.");
    }
    if (aContents == null) {
      throw new NullPointerException("Contents must not be null.");
    }
    if (!aFile.exists()) {
      throw new FileNotFoundException ("File does not exist: " + aFile);
    }
    if (!aFile.isFile()) {
      throw new IllegalArgumentException("Must not be a directory: " + aFile);
    }
    if (!aFile.canWrite()) {
      throw new IllegalArgumentException("File cannot be written: " + aFile);
    }
    Writer output =  new BufferedWriter( new FileWriter(aFile) );
    try {
      output.write( aContents );
    }
    finally {
      //flush and close both "output" and its underlying FileWriter
      output.close();
    }
  }
  public static void main (String... aArguments) throws IOException {
    File testFile = new File("C:\\Temp\\blah.txt");
    setContents(testFile, "blah blah blah");
  }
} 

Avoid @throws in javadoc

Some argue that @throws should not be used at all. Instead, one may simply rely on the javadoc tool to automatically document all exceptions placed in the throws clause.

Checked Exceptions :

  • declaring only checked exceptions in the method's throws clause is a widely followed convention
  • javadoc automatically generates basic documentation for all exceptions in the throws clause
  • the documentation is generated with no extra effort on your part
Unchecked Exceptions :
  • aren't typically placed in the method's throws clause.
  • are very rarely caught by the caller.
  • do not form part of the contract of the method, but rather represent what happens when the contract is broken. The caller cannot recover from such errors.
  • almost always occur when a precondition on a parameter is not met. However, such conditions are almost always already documented in a @param tag.
Therefore, if you :
  • only place checked exceptions in the throws clause
  • don't use @throws at all
then only checked exceptions will appear in javadoc. It can be argued that this is beneficial: since checked exceptions are more important than unchecked ones, it's best that they stand out in javadoc, without being mixed in with other exceptions of minor interest.

In almost all cases, a @throws tag simply repeats verbatim conditions already stated in a @param tag, and doesn't add in any way to the specification of the method's behavior. Such repetition should be regarded with grave suspicion. When a change occurs, it's far too easy to forget to update the javadoc in two separate places.

A general comment regarding broken contracts can be stated once in the javadoc overview.html document :
"If the requirements or promises of any method's contract are not fulfilled (that is, if there is a bug in either the method or its caller), then an unchecked exception will be thrown. The precise type of such an unchecked exception does not form part of any method's contract."

Example

BasketBall has two constructors.

The first constructor includes several @throws tags in its javadoc. However, aside from the type of the unchecked exception, all of these @throws tags are logically equivalent to some previous statement in a @param tag. They add nothing to the contract.

The second constructor follows a different style. It has a single parameter, and the conditions on this parameter are stated once (and once only) in its @param tag.

public final class BasketBall {
  /**
  * @param aManufacturer non-null and has visible content.
  * @param aDiameter in centimeters, in the range 1..50.
  * @throws IllegalArgumentException if aDiameter not in given range.
  * @throws IllegalArgumentException if aManufacturer has no visible content.
  * @throws NullPointerException if aManufacturer is null.
  */
  BasketBall( String aManufacturer, int aDiameter ){
    //..elided
  }
  /**
  * @param aDiameter in centimeters, in the range 1..50.
  */
  BasketBall( int aDiameter ){
    //..elided
  }
  // PRIVATE //
  private String fManufacturer;
  private int fDiameter;
}