1.18. Validation of Method Parameters

Problem

You need to validate parameters passed to a method. You would prefer one line of code to test that a method parameter is valid.

Solution

Use Validate to check method parameters. Validate can check for empty or null parameters, as well as evaluate any logical conditions. Example 1-18 demonstrates the use of Validate to perform a few simple validations.

Example 1-18. Using Validate to perform simple validations

import org.apache.commons.lang.Validate;

public doSomething( int param1, Object[] param2, Collection param3 ) {

    Validate.isTrue( param1 > 0, "param must be greater than zero" );
    Validate.notEmpty( param2, "param2 must not be empty" );
    Validate.notEmpty( param3, "param3 must not be empty" );
    Validate.noNullElements( param3, "param3 cannot contain null elements" );

    // do something complex and interesting
}

Discussion

Write finicky methods. Don’t just work with any input, have some standards. Don’t hang around with garbage or nulls—clean up your act, and use Validate. Methods in Validate throw an IllegalArgumentException if invalid parameters are encountered. Validate.notEmpty( ) will throw an exception if the parameter is null or empty, and Validate.isTrue( ) takes a logical expression that must evaluate to true. Table 1-5 lists a number of static methods of Validate.

Table 1-5. Available static methods on Validate

Validate method

Description

isTrue(boolean expr)

Fails if expression evaluates to false

isTrue(boolean expr, String msg)

Same as above; constructs exception with a message

noNullElements(Collection col)

Fails if collection contains a null

noNullElements(Object[] array)

Fails if array contains a null

notEmpty(Collection col)

Fails if collection is empty

notEmpty(Map map)

Fails if map is empty

notEmpty(Object[] array)

Fails if array is empty

notNull(Object obj)

Fails if object is null

Well-written code tests input and parameters. Is the number within a given range? Has the user supplied an index that is out of bounds for this array? Unless you want your applications to break down and throw exceptions, make sure that you are not trying to access the 15th element of a 10-element array; your code should be skeptical of parameters and refuse to run if it doesn’t get its way. In fragile systems, parameter validation is not a top priority; errors are dealt with as different application layers throw a potpourri of exceptions. Other, more unstable, systems will pass a null all the way down the call stack until the lowest level blows up and spits back a NullPointerException—with a long-winded stack trace. Don’t play around with null—avoid it, test your system exhaustively, and throw an IllegalArgumentException.

Consider the following method, which takes a latitude, longitude, and mode parameter. Both the latitude and longitude must fall within a valid range, and this method should throw an IllegalArgumentException if it encounters an unrealistic latitude, an unrealistic longitude, or an empty mode parameter. The Validate utility used in conjunction with a DoubleRange object can reduce the amount of code dedicated to range checking and method parameter validation. To test the validity of the coordinates, two constant DoubleRange objects are created outside of the method. DoubleRange.includesDouble(double d) returns true if the number falls within the defined range—if x <= rangeMax && x >= rangeMin. (See Example 1-19.)

Example 1-19. Using the Validator and the DoubleRange to validate method parameters

public static final DoubleRange LAT_RANGE = new DoubleRange(  -90.0,  90.0 );
public static final DoubleRange LON_RANGE = new DoubleRange( -180.0, 180.0 );

public double elevation( double latitude, 
                         double longitude,
                         String mode ) {
    Validate.notEmpty( mode, "Mode cannot be empty or null" );
    Validate.isTrue( LAT_RANGE.includesDouble( latitude ),
                      "Lat not in range " + latRange, latitude );
    Validate.isTrue( LON_RANGE.includesDouble( longitude ),
                      "Lon not in range " + lonRange, longitude );

    double elevation = 0.0;
    // code to implement the elevation method
    return elevation;
}

It takes a fair amount of institutional discipline to make sure that a code base has a sufficient amount of validation, and it is surprising how often systems scale to thousands of lines without it. Systems without good internal validation and sanity checks are characterized by frequent occurrences of NullPointerException and RuntimeException. Throwing RuntimeException in the form of IllegalArgumentException might seem like an odd recipe for increasing overall stability. But, if you actively throw exceptions and test exhaustively, it will be less toil and trouble to identify and fix defects—your application will fail sooner, and you will have short stack traces and well-defined problems. Performing rigorous parameter validation alone will not create a stable system, but it is part of a larger equation, which involves unit testing and validation.

In addition to parameter validation, you should be writing unit tests that test this validation. If you write good unit tests using JUnit and then measure your test coverage with a tool like Clover or JCoverage, you can save yourself an amazing amount of grief fixing unanticipated defects. Your unit tests should be covering close to 100% of your code, and they should throw curveballs. Don’t just test the expected situation—if your method takes a double, see what it does with Double.POSITIVE_INFINITY; and if your method takes an array, pass it a null. Test the unexpected; if you find that your method melts down with a null, check the parameter with Validate. 100% test coverage is not an unrealistic ideal; it will drive your validation code. More test coverage always leads to a higher level of system quality.

See Also

Chapter 8 discusses DoubleRange and other utilities for capturing a range of numbers.

Chapters 4 to Chapter 7 of the Java Extreme Programming Cookbook by Eric Burke and Brian Coyner (O’Reilly) are great introductions to unit testing tools and concepts. Read this book for an introduction to JUnit, HttpUnit, mock objects, and the idea of unit testing. Think of Validate as a runtime test for a method; the methods on Validate are analogous to the assert( ) methods in JUnit’s TestCase class.

If you are interested in measuring code coverage of unit tests, take some time to look at Clover from Cortex eBusiness. Cortex eBusiness offers a free license to Open Source projects, but if you are using the product in a commercial environment, it is around $250. More information about Clover is available at http://www.thecortex.net/clover/. Other than Clover, there is another tool named JCoverage, which can also be used to measure test coverage. More information about JCoverage is available at http://www.jcoverage.com/. For more information about JUnit, go to http://www.junit.org/.

Get Jakarta Commons Cookbook now with the O’Reilly learning platform.

O’Reilly members experience books, live events, courses curated by job role, and more from O’Reilly and nearly 200 top publishers.