Members

There are several different types of member that type definitions can contain. Each of them is described on this page.

Initialisers

Initialisers provide code that will be run to initialise either a type or an object of that type. They are declared as follows:

[static]
{
  <statment>*
}

Or, as an LALR(1) grammar:

Initialiser = Block | Modifiers Block

They can execute code with certain restrictions:

  • They cannot return
  • They cannot use a variable before it has been initialised, unless its type has a default value.

If a type has multiple initialisers, they are run in the order they are defined.

The only modifier which an initialiser is allowed is static, which determines whether it initialises the type or an instance of the type.

Static Initialisers

These are only run once, at the start of the program. If multiple classes have static initialisers, the order in which they run is not defined.

A static initialiser is responsible for initialising a type’s static fields and properties. Because all static fields must have a default value (e.g. null), most static fields do not need to be initialised. However, final fields and properties must be initialised exactly once. For this reason, the static initialiser is required to set them exactly once, and nothing else is allowed to assign to them again.

A static initialiser is never run in an immutable context.

Non-static Initialisers

Non-static initialisers are allowed on all types but interfaces, and are run during the constructor of their type.

They are run immediately after the super-class’s constructor, which is run just before the start of the current class’s constructor if it is not called anywhere else during the constructor. If there is no super-class, the super-constructor refers to the object type’s constructor, which is a no-op.

A non-static initialiser must assume that all of the super-class’s fields and properties have been initialised, and that super-interface properties have not yet been initialised. The information about what is initialised at the end of the initialiser is used when checking each constructor.

A non-static initialiser is run in an immutable context iff any of the constructors are immutable.

A non-static initialiser is run in a selfish context iff all of the constructors are selfish.

Fields

Fields are variables which exist as either part of a type or part of an object, depending on whether or not they are static.

A field has a name and a type, which determine how to access it and what it can be used for, they also support several modifiers, and can have an initialiser expression. They are defined as follows:

<modifier>* <type> <name> [= <expression>] ;

Or, as an LALR(1) grammar:

Field = Modifiers Type Name ;
      | Modifiers Type Name Equals Expression ;
      | Type Name ;
      | Type Name Equals Expression ;

If an initialiser expression is given, it is considered part of either the static or non-static initialiser, depending on the staticness of the field.

A field can have several different modifiers:

final
Means that the field must be initialised exactly once, either in the initialiser or the constructor.
mutable
Allows this field to be modified even in an immutable context, by reassigning its value or by altering that value’s state.
A final, immutably-typed field cannot be mutable.
static
Means that the field is part of the type rather than objects of that type.
A static field’s type must always have a default value, such as 0 or null.
since(1.2.3)
Specifies which version introduced this field.
This can be used to add new fields to an object in a binary-compatible way, because fields are sorted by their since specifier so that new fields go at the end of the binary representation of an object.

All types can have static fields, and all types except interfaces can have non-static fields.

Constructors

Constructors are called during the creation of an object. They are required to initialise all of the instance’s non-static variables. Constructors are allowed to exist on classes, compound types, and enums, but not interfaces.

A constructor can be defined as follows:

<modifier>* create ( <parameters> ) [throws <qualified-name>[, <qualified-name>]*]
{
  <statement>*
}

Or, as an LALR(1) grammar:

Constructor = Modifiers create ParameterList ThrowsClause Block
            |           create ParameterList ThrowsClause Block
            | Modifiers create ParameterList ThrowsClause ;
            |           create ParameterList ThrowsClause ;

A constructor with a semicolon instead of a block is treated as if it had an empty block. This is useful for defining constructors which take auto-assign parameters.

A constructor can have any of the following modifiers:

immutable
Means that the constructor cannot modify any static variables.
selfish
Making a constructor selfish tells the compiler that this constructor can do things with the object after it has been completely initialised. This allows the constructor to perform arbitrary method calls on this, but prohibits any subclasses from calling this constructor as a delegate (i.e. a super() call).
since(1.2.3)
Specifies which version introduced this constructor.
If multiple constructors with the same signature but different since specifiers are defined, then the most recent one (as determined at compile time) will always be used.

Methods

Methods are functions which exist either as part of a class or as part of an object, depending on whether or not they are static. Methods can exist on classes, compound types, interfaces, and enums.

A method can be defined as follows:

<modifier>* <return-type> <name> ( <parameters> ) [throws <qualified-name>[, <qualified-name>]*]
{
  <statement>*
}

Or, as an LALR(1) grammar:

Method = Modifiers Type Name Parameters ThrowsClause Block
       | Modifiers void Name Parameters ThrowsClause Block
       |           Type Name Parameters ThrowsClause Block
       |           void Name Parameters ThrowsClause Block
       | Modifiers Type Name Parameters ThrowsClause ;
       | Modifiers void Name Parameters ThrowsClause ;
       |           Type Name Parameters ThrowsClause ;
       |           void Name Parameters ThrowsClause ;

A method that provides an implementation is a method definition, whereas one that does not is a declaration. The normal way to provide an implementation is to give the method a block; however if a method written with a semicolon has an auto-assign parameter, it gets an implicit empty block.

A method declaration cannot have any default parameters.

A method with void as its return type does not return anything.

A method can have any of the following modifiers:

abstract
Means that this is a method declaration that must be defined by a sub-type.
abstract methods cannot be static or native, and can only be declared on an abstract type (such as an interface or an abstract class).
static
Means that the method is part of the type rather than part of objects of that type.
A static method cannot reference things that are only available on the instance of an object, such as this, type parameters, and non-static fields, properties, and methods.
immutable
Means that this method cannot modify any state, unless that state is explicitly marked as mutable. For example, no fields can be reassigned, and no functions that are not also immutable can be called (including property methods).
native "foo"
Gives this method the native name “foo”, so that it can either be called from native code, or call into native code.
If this method is specified as a definition, it is treated as a native up-call, and can be called from native code under the linker name “foo”.
If this method is specified as a declaration, it is treated as a native down-call, and any calls to it will call into the native function with the linker name “foo”.
since(1.2.3)
Specifies which version introduced this method.
If multiple static methods with the same signature but different since specifiers are defined, then the most recent one (as determined at compile time) will always be used.
No two non-static methods can have the same signature, even if the since specifiers differ. However, to maintain binary compatibility with older versions of a library, a new non-static method can be added to a class if its since specifier is higher than all of the since specifiers in previous versions of the library.
This is accomplished by primarily sorting the virtual function table by since specifier.

Properties

Properties are a cross between fields and methods. They are accessed using the same syntax as fields, but internally they are treated as three different methods: a getter, a setter, and a constructor, and optionally a backing variable.

The three property methods are each used in different situations:

getter
This is used to access the value of the property. It takes no parameters and must return the current property value.
By default, a property getter is an immutable function.
If no getter is provided, the default implementation is to return the backing variable’s value.
setter
This is used to overwrite the value of the property. It takes a single parameter of the same type of the property, and does not return anything.
Setters do not exist for final properties.
By default, a property setter is not an immutable function.
If no setter is provided, the default implementation is to update the backing variable’s value.
create
This is the constructor, which is used to set the initial value of the property during the object’s constructor. If it exists, the constructor will call it the first time it assigns to the property. It cannot rely on any of the object’s fields (including the backing variable) having already been initialised.
There are some complex rules about whether or not a property constructor exists:
- A final property always has a constructor.
- A static property does not have a constructor unless it is final.
- If a non-static, non-final property declares/defines a constructor, it gets one.
- If the property has a backing variable which doesn’t have a default value (e.g. null, zero), it has a constructor. (note: static properties with backing variables must always have default values)
- If the property is abstract and an implementation’s backing variable wouldn’t have a default value, it has an (abstract) constructor.
- A property does not have a constructor unless one of the previous rules applies.
By default, a property constructor is not an immutable function.
If no constructor is provided, but the property has one, the default implementation is to perform exactly the same code as the setter. If no setter exists, the default implementation is to set the backing variable’s value.

By default, a property will have a backing variable unless it is marked as abstract. However, marking it as unbacked will stop the backing variable from being created. The backing variable can only be accessed by referring to the property as a variable from inside one of the property methods.

A property can be defined as follows:

<modifier>* property <type> <name> [ = <expression> ]
<method-list> ;

Or, as an LALR(1) grammar:

Property = OptionalModifiers property Type Name PropertyMethodList ;
         | OptionalModifiers property Type Name Equals Expression PropertyMethodList ;
PropertyMethodList = ε
                   | PropertyMethodList PropertyMethod
PropertyMethod = OptionalModifiers getter
               | OptionalModifiers getter ThrowsClause Block
               | OptionalModifiers setter
               | OptionalModifiers setter ( OptionalModifiers Name ) ThrowsClause Block
               | OptionalModifiers setter Parameters ThrowsClause Block
               | OptionalModifiers setter Parameters ThrowsClause
               | OptionalModifiers create
               | OptionalModifiers create ( OptionalModifiers Name ) ThrowsClause Block
               | OptionalModifiers create Parameters ThrowsClause Block
               | OptionalModifiers create Parameters ThrowsClause

Note: the setter and create rules which take parameters but not a block are only valid when used for auto-assign parameters.

If a property method is specified without a block or any auto-assign parameters, then it is used as a hint that the default implementation should be used, while optionally changing the immutability of the method.

A property can have any of the following modifiers:

abstract
Means that the property has no implementation and needs to be overridden in a subclass.
Abstract properties are always implicitly unbacked.
final
Means that the property does not have a setter and has a constructor. A property’s backing variable is never final.
mutable
Makes the property and the backing variable both behave as mutable fields. The setter and/or constructor may need to be made immutable methods for this to work correctly when called from an immutable context.
since(1.2.3)
Specifies which version introduced this property.
If multiple static properties with the same name but different since specifiers are defined, then the most recent one (as determined at compile time) will always be used.
No two non-static properties can have the same name, even if the since specifiers differ. However, to maintain binary compatibility with older versions of a library, a new non-static property can be added to a class if its since specifier is higher than all of the since specifiers in previous versions of the library.
This is accomplished by primarily sorting both the virtual function table and the list of fields by since specifier.
static
Means that the property is part of the type rather than objects of that type.
A static property method cannot reference things that are only available on the instance of an object, such as this, type parameters, and non-static fields, properties, and methods.
unbacked
Means that this property does not have a backing variable.
If this is used, implementations must be provided for whichever property methods exist for this property.

A property method can have any of the following modifiers:

immutable
Makes the property method immutable (this is the default for getters).
mutable
Makes the property method not immutable (this is the default for setters and constructors).

A property method cannot actually declare any checked thrown types. The reason for the throws clause is to allow unchecked thrown types to be declared.

Table Of Contents

Previous topic

Type Definitions

Next topic

Type System

This Page