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 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 declared 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 is compiled 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 }
To keep Sedona lightweight and simple, the following rules apply to constructor methods:
- 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.
The constructor method for a class is called whenever an object of that type is instantiated.
For static inline fields, this happens as soon as the scode is loaded when the Sedona VM boots up.
For instance inline fields, this happens when the Sedona VM loads the Sedona application and
calls the constructors for the application's components as well as each component's inline
object fields.
If the running app is modified remotely, however, any new components will be instantiated
immediately by App.add()
, which will call the component's constructor (and all its
non-static inline object fields) at that time.
For example:
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 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.