It's a reasonable policy for a class to skip validating arguments of private
methods.
The reason is that private
methods can only be called from the class itself. Thus,
a class author should be able to confirm that all calls of a private
method are valid.
If desired, the assert
keyword can be used to verify private
method arguments,
to check the internal consistency of the class. (Using assert
for checking the arguments of
a non-private
method is not recommended, since disabling such assertions
would mean the contract of the non-private
method is no longer being enforced.)
When validating an argument, an Exception
is thrown if the test fails.
It's often one of these unchecked exceptions that are thrown:
RuntimeException
.
Checked exceptions can also be thrown,
as shown in the Model Object validation topic. Many programmers
document these exceptions in the @throws
clause of the method's javadoc, since they clearly state the method's requirements
to the caller (the pre-conditions). Others, however, feel that
manually documenting exceptions should be avoided.
If every object parameter of every method in a class needs to be non-null
in order to avoid throwing NullPointerException
, then it's acceptable
to state this once in the general class javadoc, instead of repeating it
for each method. Another alternative is to state in overview.html
(javadoc's summary description of an entire application) that all parameters
are to be considered non-null unless explicitly stated otherwise.
Example 1
The constructor of this class validates its arguments before doing anything
else. If a validation fails, then an IllegalArgumentException
is thrown.
public final class ElementaryParticle { /** @param name has content. @param speed is in the range 0 (inclusive) to 1 (inclusive), and is expressed as a fraction of the speed of light. (The photon is an example of an elementary particle which travels at this speed.) @exception IllegalArgumentException if a param does not comply. */ public ElementaryParticle (String name, double speed) { if (!textHasContent(name)) { throw new IllegalArgumentException("Name has no content."); } if (speed < 0.0 || speed > 1.0) { throw new IllegalArgumentException("Speed not in range [0..1]: " + speed); } this.name = name; this.speed = speed; } //..other methods elided // PRIVATE private String name; private double speed; /** Returns true if aText is non-null and has visible content. This is a test which is often performed, and should probably be placed in a general utility class. */ private boolean textHasContent(String text){ String EMPTY_STRING = ""; return (text != null) && (!text.trim().equals(EMPTY_STRING)); } }
Some validations are very common:
null
Args
class throws IllegalArgumentException
if a corresponding boolean check fails. Using Args
to validate
arguments will increase legibility noticeably, especially in the case of
multiple validations.
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 } }