Interfaces
Interfaces define contracts that classes can implement.
Basic Interface
interface Printable {
function print() void;
}
interface HasValue {
var value: i32;
}
class Counter implements Printable, HasValue {
function init(v: i32) {
this.value = v;
}
function print() void {
println(this.value);
}
}Visibility
Interfaces and their members are private by default and are marked public
individually. A private interface member is only callable through the
interface from inside the interface's module; a class implementing a public
member must declare its implementation public. Interface fields inherited by
a class keep the visibility they were declared with on the interface.
public module shapes {
public interface IShape {
public function area() f64;
function debug_id() i32 { return 0; } // module-internal default
}
public class Square implements IShape {
var side: f64;
public function init(s: f64) { this.side = s; }
public function area() f64 { return this.side * this.side; }
}
}Const Methods
An interface member declared const function promises not to change the
object, so it can be called through a const ref to the interface. Every
implementation must then be a const function too (see
Const Methods). A class may declare extra const
methods the interface does not require.
interface IShape {
const function area() f64;
}
function measure(s: const ref IShape) f64 { return s.area(); }Interface Fields
Interfaces can declare fields that implementing classes must have:
interface Named {
var name: i32; // Implementing class must have this field
}
class Person implements Named {
// 'name' field is inherited from Named interface
function init(n: i32) {
this.name = n;
}
}Default Implementations
Interfaces can provide default method implementations:
interface Answerable {
function answer() i32 {
return 42;
}
}
class Thinker implements Answerable {
function init() {}
}
function main() i32 {
var t = Thinker();
return t.answer(); // Uses default: returns 42
}Classes can override default implementations:
class DeepThinker implements Answerable {
function init() {}
function answer() i32 {
return 43; // Override default
}
}Generic Interfaces
Interfaces can have type parameters, allowing generic contracts:
interface Container<T> {
function get() T;
function set(value: T) void;
}
class Box<T> implements Container<T> {
var value: T;
function init(v: T) {
this.value = v;
}
function get() T {
return this.value;
}
function set(v: T) void {
this.value = v;
}
}Implementing Generic Interfaces with Concrete Types
A non-generic class can implement a generic interface with a specific type:
interface Wrapper<T> {
function unwrap() T;
}
class IntWrapper implements Wrapper<i32> {
var value: i32;
function init(v: i32) {
this.value = v;
}
function unwrap() i32 {
return this.value;
}
}Generic Class Implementing Generic Interface
When a generic class implements a generic interface, the type parameters flow through:
interface Mappable<T, U> {
function map(f: fn(T) U) U;
}
class Value<T> implements Mappable<T, T> {
var data: T;
function init(d: T) {
this.data = d;
}
function map(f: fn(T) T) T {
return f(this.data);
}
}Multiple Interfaces
Classes can implement multiple interfaces:
interface Runnable {
function run() void;
}
interface Stoppable {
function stop() void;
}
class Service implements Runnable, Stoppable {
var running: bool;
function init() {
this.running = false;
}
function run() void {
this.running = true;
}
function stop() void {
this.running = false;
}
}Builtin and Stdlib Interfaces
IError (error handling, code() and message()) is builtin and cannot be
redefined; see Builtin Types. The iteration protocol comes
from the standard library (iterator.sun, see Iteration):
IIterator<T, Container>-next(c: ref Container) Option<T>;Noneends the sequenceIIterable<T, Self>-iter()returning an iterator
using sun;
// Implementing IIterator (self-iterating pattern)
class NumberIterator implements IIterator<i32, NumberIterator> {
var current: i32;
var max: i32;
function init(start: i32, end: i32) {
this.current = start;
this.max = end;
}
function next(self: ref NumberIterator) Option<i32> {
if (this.current >= this.max) {
return Option.None;
}
var result = this.current;
this.current = this.current + 1;
return Option.Some(result);
}
}Classes can implement multiple interfaces by separating them with commas.
Attempting to redefine the builtin IError interface will result in a compilation error.