Constructors

Constructors are a special method-like type member that defines how a Class or Record gets instantiated and initialized (i.e. "constructed").

Constructors are not invoked directly, as regular methods would be. Instead, the new Expression is used to create a class instance and call one (or more) of its constructors implicitly, and the same new Expression can also be used to initialize a record.

Constructors can also call each other, deferring part of object construction to a different level, by using the constructor Expression. More on that, below.

Note: Constructors (like Finalizers and Custom Operators) are very similar in structure to regular methods, and many topics covered in the Methods topic will apply to them, as well.

This includes the sections on Parameters, Method Body and Pre- and Post-Conditions.

Constructor Declaration Syntax

A constructor declaration consists of the constructor keyword, optionally followed by a list of parameters that can be passed to the constructor, in parenthesis. Constructors cannot have a result.

The constructor declaration can optionally be followed by a number of Modifiers, separated by semicolons. The most important modifiers will be covered in more detail, below.

constructor (aParameter: String; aNotherParameter: Integer); public;

Constructor Implementation Syntax

Unless it is empty or abstract, each constructor declaration needs to provide an implementation. The implantation can be provided right behind the declaration, using what Oxygene refers to as Unified Class Syntax, or it can be provided below the type declaration in the implementation section of the file.

If the implementation is provided separately, the constructor declaration (minus any modifiers) is repeated, but expanded to include the type name in front of the method name:

constructor MyClass(aParameter: String; aNotherParameter: Integer);
begin
  // code goes here
end;

The constructor body follows the same rules as described for Methods. This includes optional legacy var sections, and Pre- and Post-Conditions.

Deferred Construction

For classes which are always part of the global class hierarchy, each constructor must, eventually, defer back to a constructor of the ancestor class, to allow it to perform its part of the initialization, as well. This can happen implicitly, or explicitly with a constructor Expression.

If no constructor Expression is present, the compiler will automatically generate a call to a matching constructor in the base class, as first step of the constructor. A matching constructor is either one with the exact same parameters as the current constructor, or one without any parameters.

In cases where no matching constructor exists, an explicit constructor call is required to let the compiler know which constructor to defer to.

A constructor can choose to defer construction to a different constructor in the same class (a so called "convenience constructor"), or to one in the base class. Even if construction is deferred within the same class, eventually one constructor in the chain must call a base constructor.

A constructor call is done by using the constructor keyword, optionally followed by whatever parameters are required for the constructor in question. By default, a constructor call defers to a constructor in the same class. The call can be prefixed with the inherited keyword in order to call the base class.

type
  Ancestor = public class
  public
    constructor(aString: String); empty;
  end;
  
  Descendant = public class(Ancestor)
  public
    constructor(aString: String); // will automatically call the base .ctor
    begin
      writeLn("I think this line is mostly filler");
    end;

    constructor(aValue: Double);
    begin
      inherited constructor("Number "+aValue.ToString); // calls the base .ctor
    end;

    constructor(aValue: Integer);
    begin
      constructor(aValue.ToString); // defers to another local .ctor
    end;

  end;

Constructor calls must happen on the top begin/end level of the constructor body; they may not be nested in other constructs, such as if/then Statements, loops or the like. They may also not be preceded by any try Block or exit Statements. Each constructor may also perform no more than one call to a different constructor.

Also note that access to self is not allowed in a constructor until after the deferred constructor call. This includes:

  • Use of self directly.
  • Access to fields of the base class.
  • Calls to methods, properties or events of the current class or the ancestor.

Constructors in Mapped Types

Some special considerations apply to constructors in Mapped Types. Please refer to the Constructors sub-topic there for details.

Initializers

Types can contain Fields and Stored Properties defined with an initializer that sets a start value for them. Initialization of these fields happens as part of the constructors, with code automatically generated by the compiler. There are certain rules that are relevant for understanding how fields will be initialized.

  • Initializers that do not require access to self will be run at the beginning of any constructor that calls a base constructor.
  • Initializers that require access to self will be run after the call to the base constructor.

This introduces two important caveats:

  1. Convenience constructors cannot rely on initializers to have run until after they have deferred to a different constructor (which in turn, will have deferred to the base class).
  2. No constructors can rely on initializers that require self to have run until after the deferred call.

Note that this only applies to explicit initializers. All fields or properties will of course be pre-set to their default value (e.g. 0 or nil) from the very beginning.

Also note that this does not apply to Properties marked with the lazy Member Modifier, which defers execution of the initializer until the first time the property is accessed.

Inheriting Constructors

If a class declares no constructors of its own, it automatically inherits all public constructors of the base class.

If the base class was abstract, any protected constructors are also inherited, and made public by default. This is to enable the common practice of declaring all constructors on an abstract class protected, to indicate that the class cannot be created.

Once a class declares one or more constructors of its own, only these constructors will be available to create instances of the class; any base constructors that are not matched become unavailable. (If this were not the case, instances of the descendant class could be created@@@ with base constructors, possibly leaving the class in an incomplete state.)

Multi-part Constructor Names

Similar to Multi-Part Method Names, constructors can provide optional names, and have those names split into multiple parts for all parameters.

If provided, it is convention for constructor names to start with the lower-case word with, followed by descriptive nouns for each parameter:

constructor withFirstName(aFirstName: String) LastName(aLastName: String);

...and when being called:

var x := new Person withFirstName("Paul") LastName("Miller");

Cocoa-Specific Concerns

On Cocoa, unnamed constructors map to init methods, while named and/or multi-part constructors map to initWith* methods. You can use either constructor syntax (recommended) or method syntax with the right naming conventions to define Cocoa class constructors.

Language Interoperability

Named multi-part constructors interoperate with all Elements languages (and Objective-C, on Cocoa). For Swift they map to the equivalent init member, dropping the with prefix, and inits defined in Swift will be available in Oxygene, RemObjects C# and Java with the with prefix. e.g.

init(fistName: String, LastName: String)

Both RemObjects C# and Java provide language extensions for defining and calling named multi-part constructors, as well.

Static Constructors

By default, constructors are applicable to individual instances of a class – that means they work on the instance being constructed.

A single parameterless static constructor can be provided by prefixing the constructor declaration with the class keyword, or by applying the static Member Modifier. This static constructor will automatically be called once (and exactly once) before the first instance of the class is created or the first static member of the class is accessed.

constructor; static; // static constructor

Regardless of what section it is declared in, the static constructor will always have private visibility.

Static fields will be initialized from an implicit class constructor.

Visibility

The visibility of (non-static) constructors is governed by the Visibility Section of the containing type the method is declared in, or the Visibility Modifiers applied to the constructors.

Virtuality

By default, constructors in Oxygene do not participate in Polymorphism, unless Class References are used – which is rare. As such, you will not generally declare constructors as virtual or override, even when overriding constructors from a base class.

The usual modifiers can be used when designing classes to be used with Class References, please refer to that topic for more details.

Other Modifiers

A number of other Member Modifiers can be applied to constructors.

See Also