WEB4J - Minimalist Java Web Application Framework

Checked versus unchecked exceptions

Unchecked exceptions : Checked exceptions : It is somewhat confusing, but note as well that RuntimeException (unchecked) is itself a subclass of Exception (checked).

Example 1

Model Objects are the data-centric classes used to represent items in a particular domain. Model Object constructors need to handle both arbitrary user input, and input from underlying database ResultSets.

Model Object constructors should throw checked exceptions :

Here is an example Model Object, taken from the WEB4J example application. Its constructor throws ModelCtorException (a checked exception) :

package hirondelle.fish.main.resto;

import java.math.BigDecimal;
import hirondelle.web4j.model.ModelCtorException;
import hirondelle.web4j.model.ModelUtil;
import hirondelle.web4j.model.Id;
import hirondelle.web4j.security.SafeText;
import hirondelle.web4j.model.Check;
import hirondelle.web4j.model.Validator;
import static hirondelle.web4j.util.Consts.FAILS;

/** Model Object for a Restaurant. */
public final class Resto {

  /**
  * Full constructor.
  *  
  * @param aId underlying database internal identifier (optional) 1..50 characters
  * @param aName of the restaurant (required), 2..50 characters
  * @param aLocation street address of the restaurant (optional), 2..50 characters
  * @param aPrice of the fish and chips meal (optional) $0.00..$100.00
  * @param aComment on the restaurant in general (optional) 2..50 characters
  */
  public Resto(
    Id aId, SafeText aName, SafeText aLocation, BigDecimal aPrice, SafeText aComment
  ) throws ModelCtorException {
    fId = aId;
    fName = aName;
    fLocation = aLocation;
    fPrice = aPrice;
    fComment = aComment;
    validateState();
  }
  
  public Id getId() { return fId; }
  public SafeText getName() {  return fName; }
  public SafeText getLocation() {  return fLocation;  }
  public BigDecimal getPrice() { return fPrice; }
  public SafeText getComment() {  return fComment; }

  @Override public String toString(){
    return ModelUtil.toStringFor(this);
  }
  
  @Override public  boolean equals(Object aThat){
    Boolean result = ModelUtil.quickEquals(this, aThat);
    if ( result ==  null ) {
      Resto that = (Resto) aThat;
      result = ModelUtil.equalsFor(this.getSignificantFields(), that.getSignificantFields());
    }
    return result;
  }
  
  @Override public int hashCode(){
    if ( fHashCode == 0 ){
      fHashCode = ModelUtil.hashCodeFor(getSignificantFields());
    }
    return fHashCode;
  }
  
  // PRIVATE //
  private final Id fId;
  private final SafeText fName;
  private final SafeText fLocation;
  private final BigDecimal fPrice;
  private final SafeText fComment;
  private int fHashCode;
  private static final BigDecimal ZERO_MONEY = new BigDecimal("0.00");

  private void validateState() throws ModelCtorException {
    ModelCtorException ex = new ModelCtorException();
    if ( FAILS == Check.optional(fId, Check.range(1,50)) ) {
      ex.add("Id is optional, 1..50 chars.");
    }
    if ( FAILS == Check.required(fName, Check.range(2,50)) ) {
      ex.add("Restaurant Name is required, 2..50 chars.");
    }
    if ( FAILS == Check.optional(fLocation, Check.range(2,50)) ) {
      ex.add("Location is optional, 2..50 chars.");
    }
    Validator[] priceChecks = { 
      Check.min(ZERO_MONEY), 
      Check.max(new BigDecimal("100.00")), 
      Check.numDecimals(2)
    };
    if ( FAILS == Check.optional(fPrice, priceChecks)) {
      ex.add("Price is optional, 0.00 to 100.00.");
    }
    if ( FAILS == Check.optional(fComment, Check.range(2,50))) {
      ex.add("Comment is optional, 2..50 chars.");
    }
    if ( ! ex.isEmpty() ) throw ex;
  }
  
  private Object[] getSignificantFields(){
    return new Object[] {fName, fLocation, fPrice, fComment};
  }
}
 


Example 2

Args is a convenient utility class. It performs common validations on method arguments. If a validation fails, then it throws an unchecked exception. It is suitable for checking the internal consistency of program, but not for checking arbitrary user input.


package hirondelle.web4j.util;

import java.util.regex.*;

/**
* Utility methods for common argument validations.
*
*<P>Replaces <tt>if</tt> statements at the start of a method with 
* more compact method calls.
* 
* <P>Example use case.
* <P>Instead of :
* <PRE>
* public void doThis(String aText){
*   if (!Util.textHasContent(aText)){
*     throw new IllegalArgumentException();
*   }
*   //..main body elided
* }
* </PRE>
* <P>One may instead write :
* <PRE>
* public void doThis(String aText){
*   Args.checkForContent(aText);
*   //..main body elided
* }
* </PRE>
*/
public final class Args {
  
  /**
  * If <code>aText</code> does not satisfy {@link Util#textHasContent}, then 
  * throw an <code>IllegalArgumentException</code>.
  *
  * <P>Most text used in an application is meaningful only if it has visible content.
  */
  public static void checkForContent(String aText){
    if( ! Util.textHasContent(aText) ){
      throw new IllegalArgumentException("Text has no visible content");
    }
  }

  /**
  * If {@link Util#isInRange} returns <code>false</code>, then 
  * throw an <code>IllegalArgumentException</code>. 
  *
  * @param aLow is less than or equal to <code>aHigh</code>.
  */
  public static void checkForRange( int aNumber, int aLow, int aHigh ) {
    if ( ! Util.isInRange(aNumber, aLow, aHigh) ) {
      throw new IllegalArgumentException(aNumber + " not in range " + aLow + ".." + aHigh);
    }
  }

  /**
  * If <tt>aNumber</tt> is less than <tt>1</tt>, then throw an 
  * <tt>IllegalArgumentException</tt>.
  */
  public static void checkForPositive(int aNumber) {
    if (aNumber < 1) {
      throw new IllegalArgumentException(aNumber + " is less than 1");
    }
  }

  /**
  * If {@link Util#matches} returns <tt>false</tt>, then 
  * throw an <code>IllegalArgumentException</code>. 
  */
  public static void checkForMatch(Pattern aPattern, String aText){
    if ( ! Util.matches(aPattern, aText) ){
      throw new IllegalArgumentException(
        "Text " + Util.quote(aText) + " does not match '" +aPattern.pattern()+ "'"
      );
    }
  }
  
  /**
  * If <code>aObject</code> is null, then throw a <code>NullPointerException</code>.
  *
  * <P>Use cases :
  <pre>
   doSomething( Football aBall ){
     //1. call some method on the argument : 
     //if aBall is null, then exception is automatically thrown, so 
     //there is no need for an explicit check for null.
     aBall.inflate();
    
     //2. assign to a corresponding field (common in constructors): 
     //if aBall is null, no exception is immediately thrown, so 
     //an explicit check for null may be useful here
     Args.checkForNull( aBall );
     fBall = aBall;
     
     //3. pass on to some other method as parameter : 
     //it may or may not be appropriate to have an explicit check 
     //for null here, according the needs of the problem
     Args.checkForNull( aBall ); //??
     fReferee.verify( aBall );
   }
   </pre>
  */
  public static void checkForNull(Object aObject) {
    if ( aObject == null ) {
      throw new NullPointerException();
    }
  }
  
  // PRIVATE //
  private Args(){
    //empty - prevent construction
  }
  
}
 



See Also :
Model Objects
Would you use this technique?
Yes   No   Undecided   
Add your comment to this Topic :

© 2008 Hirondelle Systems | Source Code | Contact | License | Quotes | RSS
Individual code snippets can be used under this license - Last updated on September 6, 2008.
Over 98,000 visits last month - Built with WEB4J.
- In Memoriam : Bill Dirani -