Concepts / The Canonical Constructor

The Canonical Constructor

An additional constructor is any constructor in a class other than the canonical constructor, and it gives callers an alternative way to initialize an object.

  • Programming

Why One Name Is Not Enough

Imagine a FullName class that normally stores two pieces of information: a first name and a last name. Most people have both, so the usual way to create an object takes both values. But some people go by only one name, and a program that models people should still be able to represent them. This is the problem additional constructors solve. They give a class more than one way to be built, each suited to a different kind of input.

In this article you will trace a small constructor chain step by step, predict which fields end up holding which values, and then see how the main constructor can be customized to reject bad input. Try to reason about each prediction yourself before reading the explanation that follows it.

Additional Versus Canonical Constructors

Definition

A canonical constructor takes parameters to initialize an object's fields, and it can be customized with additional code.

Definition

An additional constructor is any constructor in a class other than the canonical constructor, and it gives callers an alternative way to initialize an object.

The two kinds of constructor differ in what they accept, and in who does the final setting of the fields. The canonical constructor takes the full set of values and assigns them to the fields. An additional constructor takes a different set of inputs, and its job is to get those inputs into a form the canonical constructor can use. In the FullName example, the canonical constructor takes two parameters, a first name and a last name, while the additional constructor takes only one.

Tracing a One-Name Call

java
Notes on the code
  • The record header declares the two components, firstName and lastName, which the canonical constructor sets.
  • The one-parameter constructor is an additional constructor because it is not the canonical one.
  • this(onlyName, null) selects the two-parameter constructor and supplies the arguments.

Suppose a caller writes new FullName('Ada'). The one-parameter constructor runs first, with onlyName holding the value 'Ada'. Its only statement is this(onlyName, null). The keyword this followed by parentheses refers to a constructor of the same class rather than to a method or a field. Here it selects the two-parameter constructor, which is the canonical one in this class, and passes it two arguments: 'Ada' and null.

Control now moves to the canonical constructor. That constructor sets the firstName field to the value of its first argument, which is 'Ada', and the lastName field to its second argument, which is null. When it finishes, control returns to the one-parameter constructor, which has nothing left to do. The result is an object whose firstName is 'Ada' and whose lastName is null.

new FullName('Ada')FullName(onlyName)onlyName = 'Ada'this(onlyName, null)delegation callFullName(firstName,lastName)canonical constructorfirstName = 'Ada'Object returnedlastName = nullselects constructorruns its only statementcontrol movesassignsassignsconstruction endsconstruction ends
When a one-parameter constructor calls this(...), in what order does control pass between constructors, and where does the call finally stop?

Constructor Chaining and Shared Assignments

Notice that the one-parameter constructor never assigns either field directly. It delegates that assignment entirely to the canonical constructor. This is why the pattern is called constructor chaining: one constructor calls another, which in turn may do the real work. Delegating keeps the field assignments in one place, which reduces duplication and makes later changes safer. If the canonical constructor later needs to set a field differently, that change is made once, and every additional constructor that chains to it benefits.

QuestionCanonical constructorAdditional constructor
What does it accept?A value for every field, such as firstName and lastNameA different set of inputs, such as only onlyName
What does it delegate to?Nothing; it is the destination of delegationThe canonical constructor, through this(...)
Which one sets the fields?It does, directlyIt does not assign fields itself in this example
Can it be customized?Yes, with extra code in its bodyIt is written as an alternative entry point

Canonical and additional constructors in the FullName class

Now compare the one-name route with the two-name route. A caller who supplies both a first and last name uses the canonical constructor directly, so no chaining happens at all. Both routes end at the same place, the canonical constructor, which means both produce objects that are set up the same way. The only difference is how the arguments reached it.

Customizing the Canonical Constructor

A canonical constructor can be extended with extra code. The customized form omits the parameter list. In the source's syntax, the definition is written as public FullName followed by a block of code, with no parentheses after the class name. This can look as though the parameters have disappeared, but they have not. A call to the constructor still requires two arguments, and those arguments still initialize firstName and lastName.

java
Notes on the code
  • There is no parameter list after FullName, but the components firstName and lastName are still the inputs.
  • The if statement is the extra logic that runs in the constructor body.
  • The throw statement stops construction when the check fails.

Order of Operations in a Canonical Constructor

Parameters initialize the fields before the constructor's own code is executed. This ordering explains why validation in the body works. The arguments are first used to fill the fields, and only then does the constructor's extra code run. So when the check tests whether firstName is null, the value it tests is the one the caller supplied.

CallerCanonical constructorFieldsConstructor body1. pass arguments2. parameters fill fields3. run extra code4. object handed back
In what sequence do the parameter values, the constructor's own code, and the field assignments happen when an object is created?

The diagram reads as a sequence. The call supplies values, the values fill the fields, the body performs its extra work, and only then is the object handed back. The body is the only place where you can enforce rules about what the fields contain, and it runs after the fields have been filled.

Where a Validation Failure Happens

Consider two calls to the customized FullName record. In the first, new FullName(null, 'Lovelace') is called. The fields are initialized first, so firstName receives null. The check then tests firstName, finds it null, and the throw statement runs. The failure happens inside the constructor body, during the construction call itself. No object is returned to the caller.

In the second call, only the first argument changes, to new FullName('Ada', 'Lovelace'). The fields are initialized the same way, but the test is false, the throw statement is skipped, and the constructor completes normally. The same code path produces both outcomes, and the difference comes entirely from the value the caller passed in.

Call with firstNameFields initializedfirstName is null?IllegalArgumentExceptionthrownObject createdparameters firstbody runsyes: new FullName(null, ...)no: new FullName('Ada', ...)
When a check in a customized canonical constructor fails, which step is skipped, where is the exception thrown, and does the object ever get created?

What do you think happens?

When new FullName(null, 'Lovelace') runs, does the caller ever receive a FullName object?

Reveal answer
Answer:

No. The check fails inside the constructor body and the exception is thrown, so construction stops and no object is returned.

The fields were already filled with null before the check ran, but the object is never handed back to the caller because the throw statement interrupts construction at the call site.

Mistakes When Writing Alternative Constructors

  • Common mistake

    Assigning the fields directly in the one-parameter constructor as well as delegating

    What it looks like
    FullName(String onlyName) { firstName = onlyName; this(onlyName, null); }
    Why it fails

    The this(...) call must be the first statement in the constructor, and assigning fields in the one-parameter constructor duplicates work that belongs in the canonical constructor.

    Do this instead

    Keep the one-parameter constructor to a single this(onlyName, null) call so that all field assignments happen in one place.

  • Common mistake

    Expecting the customized constructor to need no arguments because the parameter list is missing

    What it looks like

    new FullName() used with the customized record that has no parameter list

    Why it fails

    Dropping the parameter list does not remove the parameters; calls still supply the components.

    Do this instead

    Pass one argument for each component, such as new FullName('Ada', 'Lovelace').

  • Common mistake

    Checking a field expecting the check to see a default value instead of the caller's argument

    Why it fails

    Parameters initialize the fields before the body runs, so the body sees the values that were passed in.

    Do this instead

    Write checks against the field names, knowing they already hold the supplied values.

  • Common mistake

    Looking for the failure at the later point where the object is used, rather than at construction

Practice Tracing Constructor Calls

MEDIUM

Using the customized FullName record with the null check, predict what happens for each of these calls: new FullName('Grace', 'Hopper'), new FullName(null, 'Turing'), and new FullName('Alan', null). For each, say which step is reached first, whether the check passes, and whether an object is created.

Hints
  • The fields are filled from the arguments before the check runs.
  • Only the value of firstName is tested by the check in this example.
  • A failed check stops construction at the call site, so no object is returned.

Key Takeaways

  • An additional constructor is any constructor other than the canonical constructor, and it gives callers an alternative way to build an object.
  • A one-parameter constructor can delegate to the canonical constructor with this(...), so that the field assignments happen in one place.
  • A customized canonical constructor omits the parameter list but still receives the arguments, which initialize the fields before the constructor body runs.
  • A validation check in the body sees the supplied values, and when it fails the exception is thrown during the construction call, so no object is returned.