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.
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
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;
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 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
end level of the constructor body; they may not be nested in other constructs, such as
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
- Access to fields of the base class.
- Calls to methods, properties or events of the current class or the ancestor.
Constructors in Mapped Types
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
selfwill be run at the beginning of any constructor that calls a base constructor.
- Initializers that require access to
selfwill 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
selfto 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.
nil) from the very beginning.
If a class declares no constructors of its own, it automatically inherits all
public constructors of the base class.
If the base class was
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");
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.
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)
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
Static fields will be initialized from an implicit class constructor.
By default, constructors in Oxygene do not participate in Class References are used – which is rare. As such, you will not generally declare constructors as
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.
A number of other Member Modifiers can be applied to constructors.