Overview
Methods are functions used to implement executable behavior. The following keywords may be applied to a method declaration:
static: class based method versus instance basedvirtual: indicates method that may be polymorphically overriddenabstract: indicates a pure virtual methodoverride: required to indicate an override of an inherited methodnative: indicates a method implemented in native C or Java codeaction: promotes a method to a component action
In addition to the keywords above, a method may be annotated with
a protection scope keyword.
If no protection scope is specified, then public
is assumed.
Return Values
If the method returns a value to the calling function, the
return value type is included in the function definition.
If the method does not return anything, the keyword void
is used instead of a return type.
class Example
{
void echo() { Sys.out.print("echo").nl() } // returns nothing
int add(int x, int y) { return x + y } // returns an int value
}
In Sedona, a method may return any primitive type, or a reference to any built-in or user-defined class type; the only exception is an action method, which always returns void.
Static Methods
Static methods are prefixed with the static
keyword. Static methods are essentially global functions
scoped within a class name. They are declared just like
Java methods:
class Example
{
static void echo() { Sys.out.print("echo").nl() }
static int add(int x, int y) { return x + y }
}
Static methods are called with an implict or explicit type literal:
Example.echo() // explicit int five = add(2, 3) // implicit (only inside Example or subclasses)
Instance Methods
Instance methods are declared whenever the static keyword
is omitted. Instance methods contain an implicit this parameter,
which is the instance the method is called on:
class Example
{
int add() { return x + y }
int sub() { return this.x - this.y }
int x
int y
}
Note in the example, that every instance method has an implicit
parameter accessed via the this keyword. Instance methods
are called with an implict or explicit instance:
add() // implicit against this this.sub() // explicit against this x.sub() // explicit against x x?.sub() // null safe call
See Safe Navigation for how to use the "?." operator.
Virtual Methods
Virtual methods are designed to be overridden by a subclass to enable
polymorphism. Methods must be marked using the virtual keyword
before they can be overridden by subclasses. Subclasses must declare
they are overriding a method using the override keyword:
class Animal extends Virtual
{
virtual void talk() { Sys.out.print("generic\n") }
}
class Cat extends Animal
{
override void talk() { Sys.out.print("meow\n") }
}
animal.talk() // prints generic
cat.talk() // prints meow
By default an overridden method cannot itself be overridden by a subsequent
subclass. In order for Cat.talk() to be overridden by the subclass
Kitten, it must include again the keyword virtual:
class Cat extends Animal
{
override virtual void talk() { Sys.out.print("meow\n") } // override AND virtual
}
class Kitten extends Cat
{
override void talk() { Sys.out.print("mew!\n") } // this now compiles
}
kitten.talk() // prints mew!
Classes that declare virtual methods must derive from sys::Virtual.
Be aware that virtual classes have the overhead of an extra pointer for
their vtable (typically 4 extra bytes).
Abstract Methods
Abstract methods are virtual methods without an implementation. They
are declared using the abstract keyword. Abstract methods
are implied to be virtual - it is an error to use both the abstract
and virtual keyword. Abstract methods must not provide a
method body. The containing class must also be abstract.
Super
By default any virtual method call with an implicit or explicit target
of this invokes the most specific version of that method.
You can use the super keyword to invoke the super class
version of a method:
class Kitten extends Cat
{
override void talk() { super.talk() }
}
kitten.talk() // now prints meow
Constructors
A class may have one constructor, which compiles into a method called
_iInit. Whenever a class declares instance fields with
a default value, the compiler auto-generates
a constructor for you. A class may declare an explicit constructor using
a syntax similiar to Java:
final class BufInStream extends InStream
{
BufInStream(Buf abuf) { this.buf = abuf }
Buf buf
}
A class may only declare one explicit constructor (parameter overloading
is not supported). A class with an explicit constructor must
be marked final. Subclasses of sys::Component
cannot declare a constructor with parameters. Declared constructors are
used with unsized classes to
specify an array length.
All object allocation in happens through static memory
management or when a new component is created via App.add.
When allocating a component via App.add the constructor
is called automatically. All other instances get their constructors
called whenever they are declared as an inline field:
class Foo
{
// static constructor calls
static inline Buf(100) buf
static inline BufInStream(buf) in
static inline BufOutStream(buf) out
static inline Foo inst
// instance constructor calls
inline Buf(20) ibuf
}
Inline static fields are initialized in declaration order on VM startup,
which calls the appropriate constructors. These constructor calls often
result in instance constructor calls, which in turn recursively chain for
nested inline fields.
For the example above, the compiler will create the following code;
Foo._sInit is then automatically called when the VM boots:
static void Foo._sInit()
{
Foo.buf._iInit(100)
Foo.in._iInit(Foo.buf)
Foo.out._iInit(Foo.buf)
Foo.inst._iInit()
}
void Foo._iInit()
{
this.ibuf._iInit(20)
}
Native Methods
The native keyword is used on methods that are implemented
in C and Java code. Like abstract methods, native methods must not define
a body. See Native Methods for more details.
Action Methods
Methods may be annotated with the action keyword
to promote the method into a Component action. Actions must
be instance methods on a subclass of sys::Component.
See Component Actions
for more details.