Constructors in general
Constructors:
- if super or this are called, they can only be called on the first line of the constructor
- an exception can be thrown if a parameter is invalid
- you should ensure that the constructed object is in a valid state
- constructors should never call an overridable method (an overridable method is one which is neither private, static, nor final)
- constructors are never synchronized
- constructors can create thread objects, but the thread should not be started within a constructor
- constructors shouldn't pass a this reference to other objects
- constructors can be private, in order to restrict construction
- the default constructor is the constructor provided by the system in the absence of any constructor provided by the programmer. Once a programmer supplies any constructor whatsoever, the default constructor is no longer supplied.
- a no-argument constructor, on the other hand, is a constructor provided by the programmer which takes no arguments.
Example
Resto is an immutable class, since its state cannot change after construction. Note that:
- all validation is performed in its constructor
- almost all of its javadoc is concerned with stating precisely what is to be passed to the constructor
package hirondelle.fish.main.resto; import hirondelle.web4j.model.ModelCtorException; import hirondelle.web4j.model.ModelUtil; import hirondelle.web4j.model.Id; import hirondelle.web4j.security.SafeText; import hirondelle.web4j.model.Decimal; import static hirondelle.web4j.model.Decimal.ZERO; 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, Decimal 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 Decimal 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 Decimal fPrice; private final SafeText fComment; private int fHashCode; private static final Decimal HUNDRED = Decimal.from("100"); 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.range(ZERO, HUNDRED), Check.numDecimalsAlways(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}; } }
See Also :
Validate method arguments
Validate state with class invariants
Initializing fields to 0-false-null is redundant
Immutable objects
Private constructor
Avoid JavaBeans style of construction
Constructors shouldn't call overridables
Don't pass 'this' out of a constructor
Constructors shouldn't start threads
Validate state with class invariants
Initializing fields to 0-false-null is redundant
Immutable objects
Private constructor
Avoid JavaBeans style of construction
Constructors shouldn't call overridables
Don't pass 'this' out of a constructor
Constructors shouldn't start threads
Would you use this technique?