06-classes
Chapter 6: Classes
A class is a form of type, common in object-oriented programming. Values
called instances can be created from a (non-abstract) class; an instance x
created from class X
satisfies the relation x is X
.
Classes can also relate via subtyping. If a class Y
is declared a subclass of
a class X
(that is, Y <: X
), then y is Y
implies y is X
.
Parts of the class declaration's body are called class members, and are
either fields or methods. A field represents data kept within an instance.
A method represents a callable behavior of an instance. A method is like a
function, except that its body also binds the identifier self
to the instance
on which the method was called.
6.1 Class declarations
<class-declaration> ::= "class"
<identifier>
("[" <type-parameter-list> "]")?
("<:" <type>)?
<class-body>
<semicolon>
<class-body> ::= "{" <class-member>* "}"
<class-member> ::= <field>
| <method>
6.2 The @abstract
annotation
An @abstract
annotation on a class declaration means that the class cannot
be instantiated. The generated constructor throws an exception when called.
6.3 Fields
A field represents an internal variable of a class, and data particular to instances of that class.
<field> ::= "has"
<identifier>
"?"?
(":" <type>)?
("=" <expression>)?
<semicolon>
6.4 The @getter
annotation
By default, fields are internal and not accessible for reading outside of the
instance in which they are held. A @getter
annotation ensures this access,
providing a zero-parameter method (of the same name as the field) which returns
the value of the field. The return type of the method is the same as the
declared type of the field, if any.
The @getter
annotation optionally takes a single parameter in the form of an
identifier; if provided, this identifier is used for the method name instead of
the field name.
A method may not be declared in the class body with the same name as the one
provided (by default or explicitly) by the @getter
annotation; doing so
counts as a duplicate declaration, and is signaled as a compile-time error.
6.5 The @setter
annotation
By default, fields are internal and not accessible for writing outside of the
instance in which they are held. A @setter
annotation ensures this access,
providing a one-parameter method (of the same name as the field), which
returns the value of the parameter. The type of both the paramter and the
return value of the method is the same as the declared type of the field, if
any. The body of the method assigns the value of the parameter to the field.
The @setter
annotation optionally takes a single parameter in the form of an
identifier; if provided, this identifier is used for the method name instead of
the field name.
A method may not be declared in the class body with the same name as the one
provided (by defulat or explicitly) by the @setter
annotation; doing so
counts as a duplicate declaration, and is signaled as a compile-time error.
However, annotating a field with both @getter
and @setter
is explicitly
allowed, and creates a single method which is able to both return the value
bound by a field, and set the field's value from a provided argument.
6.6 The @required
and @optional
annotations
A field is required by default, which means that the class's constructor has
a required named parameter for the field. A @required
annotation reaffirms
this, but is essentially a no-op.
Either of the following two are equivalent: an @optional
annotation on a
field, or a ?
modifier on the field. These mean that the class's constructor
has an optional named parameter for the field. A field whose value is not
passed via the named parameter in its constructor is instead initialized via
the value provided via its @default
or @builder
annotation (which see), or
none
if no such annotations are present. The presence of either @default
or
a @builder
annotation means the field is optional.
6.7 The @default
annotation
A @default
annotation expects a single argument, which is parsed as an
expression. A field not initialized via the corresponding named parameter to
the constructor, is instead initialized by evaluating this expression. The
expression is evaluated in a context where self
is bound to the instance
being constructed.
A @default
annotation on a field is compatible with an @optional
annotation, but not with a @required
annotation. Using @default
and
@required
together signals a compile error.
6.8 The @builder
annotation
A @builder
annotation expects a single argument, an identifier which resolves
to a method available in the class. A field not initialized via the
corresponding named parameter to the constructor, is instead initialized by
calling this method. The method needs to accept zero arguments; referencing a
method which does not accept zero arguments signals a compile error.
A @builder
annotation on a field is compatible with an @optional
annotation, but not with a @required
annotation. Using @builder
and
@required
together signals a compile error.
A @builder
annotation is incompatible with a @default
annotation, and using
these two together signals a compile error.
6.9 The @type
annotation
6.10 The @lazy
annotation
6.11 Methods
<method> ::= "method"
<identifier>
("[" <type-parameter-list> "]")?
"(" <parameter-list> ")"
(":" <type>)?
<block>
<semicolon>
The self
identifier is bound both in the parameter list (available in any
default expressions) and in the method body. In this scope, the self
identifier is bound to the instance on which the method was called.