
Originally Posted by
ramoq
So Essentially it's OK for me to do all the null checking I want, all the validation I desire on any parameters as long as I throw back valid RTE's? Whether custom or not? (Most likely custom wrapped RTE's seem most favourable in a service scenario). The NO-NO is doing all that validation and throwing back Checked Exceptions?
Am I understanding this correctly?

That's correct. I know I have been using a zip code class as an example a few times in other threads...
Here's one version of such class from one of my old projects (very specific requirements.) It probably has more stuff than you are interested in but it demonstrates the internal basic validation and exception throwing.
Code:
package com.xxxxxxx.address.domain;
import com.xxxxxx.common.domain.InputParsingException;
import java.io.Serializable;
import java.text.MessageFormat;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
/**
* This class implements a US Zip Code object. An instance may be
* created with an empty code, or a valid non-empty code that satisfies
* the US ZIP format. No instances of this class may be created with
* invalid data. A RTE will be thrown if an attempt is made to pass
* invalid data.
*
* @author constv
*/
public class ZipCode implements Serializable {
private static final String ERR_MSG_INVALID_ZIP_FORMAT =
"Invalid Zip Code format: \"{0}\". The Zip Code must be either 5 digits, 9 digits, or 5 digits followed by a dash and 4 more digits.";
private static final String ERR_MSG_INVALID_NULL_IN_2ARG_CONSTRUCTOR =
"Invalid invocation of constructor ZipCode(String zip5, String zip4). The 5-digit code field may not be null.";
/**
* The regular expression that validates the format of the zip code input string. It allows the following forms of
* input:
* <pre>
* ##### - a 5-digit code
* #####-#### - a 9-digit code with a dash between the 5-digit code and the 4-digit extension
* ######### - a 9-digit code without a dash
* #####- - a 5-digit code with a dash after the first 5 digits but no 4-digit extension
* <p/>
* </pre>
*/
@SuppressWarnings({"HardcodedFileSeparator"})
private static final String ZIP_REGEXP = "^\\d{5}[\\-]?(\\d{4})?$";
private char[] zip5;
private char[] zip4;
/**
* Constructs a <tt>ZipCode</tt> object with no data, which could be a valid case for addresses where no zip code is
* specified, i.e. a zip code is an empty line.
*/
public ZipCode() {
}
/**
* Constructs a <tt>ZipCode</tt> object from the given input string. The constructor allows an empty string to be
* passed. If the input string is not empty, it is validated against the acceptable zip code formats.
*
* @param zip string of characters for the Zip code; the valid input must be either 5 digits, or 5 digits followed
* by a dash and 4 more digits.
* @throws com.imagitas.common.domain.InputParsingException
* if the input string does not conform to the zip code format
*/
public ZipCode(String zip) {
if (zip != null && zip.length() > 0) {
parse(zip);
}
}
/**
* Constructs a <tt>ZipCode</tt> object from two separate fields: the 5-digit code and the 4-digit add-on, if the
* latter is not empty. This is just a convenience constructor to save the client from performing some extra string
* concatenation.
*
* @param zip5Str 5-digit zip code; may not be null.
* @param zip4Str 4-digit add-on
*/
public ZipCode(String zip5Str, String zip4Str) {
if (zip5Str == null) {
throw new InputParsingException(ERR_MSG_INVALID_NULL_IN_2ARG_CONSTRUCTOR);
}
if (zip4Str != null) {
parse(new StringBuilder(9).append(zip5Str).append(zip4Str).toString());
} else {
parse(zip5Str);
}
}
/**
* Sets the zip code on an already constructed instance of the ZipCode class.
*
* @param zip zip code string
*/
public void setZip(String zip) {
if (zip != null && zip.length() > 0) {
parse(zip);
}
}
/**
* Sets the zip5 code on an already constructed instance of the ZipCode class.
*
* @param zip zip code string
*/
public void setZip5(String zip) {
setZip(zip);
}
/**
* Gets the Zip code as the 5-digit code.
*
* @return zip code as the 5-digit string
*/
public String getZip5() {
if (zip5 == null) {
return "";
} else {
return new String(zip5);
}
}
/**
* Gets all the digits of the zip code (5 or 9) without a dash.
*
* @return 5 or 9 digits of the zip code if the zip is defined
*/
public String getZipAllDigits() {
return new StringBuilder(9).append(getZip5()).append(getZip4Ext()).toString();
}
/**
* Indicates whether this zip code contains the 4-digit extension.
*
* @return boolean <tt>true</tt> or <tt>false</tt>
*/
public Boolean hasZip4Ext() {
return zip4 != null;
}
/**
* Gets the extending 4-digit part of the zip code.
*
* @return zip code's 4-digit part
*/
public String getZip4Ext() {
if (zip4 == null) {
return "";
} else {
return new String(zip4);
}
}
/**
* Gets the full zip code string if both the 5-digit and 4-digit parts are available; if only the stored sip code is
* represented by 5 digits only, it is returned as a 5-digit code.
*
* @return zip code string
*/
public String getFullZip() {
if (zip4 == null) {
return getZip5();
} else {
return new StringBuilder(9).append(zip5).append('-').append(zip4).toString();
}
}
/**
* Returns the Zip Code string.
*
* @return zip code string
*/
@Override
public String toString() {
return getFullZip();
}
/**
* Validates and parses the input string into the zip code fields. The input string is validated using the {@link
* #ZIP_REGEXP} regular expression.
*
* @param zip zip code string
*/
private void parse(String zip) {
validate(zip); // validate against regular expression
zip = zip.replace("-", ""); // after string was successfully validated, remove dash, if present
this.zip5 = new char[5]; // create and populate 5-digit field
zip.getChars(0, 5, this.zip5, 0);
if (zip.length() == 9) { // if 4-digit extension present, create and populate that field
this.zip4 = new char[4];
zip.getChars(5, 9, this.zip4, 0);
}
}
/**
* Validates the input string for matching the requirements for a zip code.
*
* @param zip string to validate
*/
private static void validate(String zip) {
if (!isValid(zip)) {
throw new InputParsingException(MessageFormat.format(ERR_MSG_INVALID_ZIP_FORMAT, zip));
}
}
/**
* Validates the input string for matching the requirements for a zip code. This <i>utility</i> method may be used
* to check if the input string matches any of the valid ZIP code formats without creating an instance of the
* ZipCode class.
*
* @param zip string to validate
* @return true if the input string matches a valid ZIP code format
*/
public static boolean isValid(String zip) {
Pattern p = Pattern.compile(ZIP_REGEXP);
Matcher matcher = p.matcher(zip);
return matcher.matches();
}
}
Note that the class is designed not to allow invalid instances to be created. If the input is completely out-of-wack and no valid ZIP code can be constructed, a meaningful runtime exception (with detail message) will be thrown. (It will be up to the application to catch that in an appropriate place, e.g. where the ZIP population occurs. A more generic and simple way - when used in a form - is to let Spring catch it during binding and produce a MethodInvokation exception that would later be mapped to the message key, like "methodInvocation.zip" during validation. But these details may be of no interest to you now, and I had some specific requirements t implement it that way. In many cases of course, all you need is a simple string for the ZIP code + simple field validation in some AddressValidator. )