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:
- 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).
- 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 through the 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 init
s 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
- Methods
constructor
Expressionsinherited
Expressionsnew
Expressionsself
Expressions- Mapped Types