Interfaces
Accept different types through one set of methods.
On this page
An interface lists methods that different classes provide. A function that takes the interface accepts any of those classes.
Declare and implement#
interface Shape {
fn area() float;
get name: String;
}
class Square is Shape {
side: float
fn area() float { return this.side * this.side }
get name: String { return "square" }
}
class Circle is Shape {
radius: float
fn area() float { return 3.14 * this.radius * this.radius }
get name: String { return "circle" }
}
fn report(shape: Shape) {
println(shape.name + ": " + shape.area())
}
fn main() {
report(Square { side: 2.0 }) // square: 4
report(Circle { radius: 1.0 }) // circle: 3.14
}
- Interface methods end in
;. Getters are declared withget name: Type;. - A class lists its interfaces after
is, separated by commas:class User is Named, Printable. - Each method must match the declared arguments, return type and error type.
- Only classes implement interfaces, not structs.
Default methods#
An interface method with a body is used by classes that do not define their own:
interface Greeter {
get name: String;
fn greet() String {
return "Hello, " + this.name
}
}
class Person is Greeter {
first: String
get name: String { return this.first }
}
class Robot is Greeter {
get name: String { return "R2" }
fn greet() String { return "Beep" }
}
fn main() {
let list = Array[Greeter]{ Person { first: "Ada" }, Robot {} }
each list as item : println(item.greet())
// Hello, Ada
// Beep
}
Errors in interface methods#
An interface method can declare an error. A class method that never fails may still implement it; callers through the interface handle the error, direct callers do not:
error LoadError (missing)
interface Source {
fn load() String !LoadError;
}
class Fixed is Source {
text: String
fn load() String { return this.text }
}
fn main() {
let fixed = Fixed { text: "data" }
println(fixed.load()) // no handler needed
let source: Source = fixed
println(source.load() !? "") // data
}
Check the concrete class#
is_a tests which class an interface value holds. match also gives the object as that class. A default case is required, because other classes may implement the interface:
interface Animal {
fn sound() String;
}
class Dog is Animal {
fn sound() String { return "woof" }
fn fetch() String { return "fetching" }
}
class Cat is Animal {
fn sound() String { return "meow" }
}
fn main() {
let pet: Animal = Dog {}
if pet is_a Dog : println("a dog")
match pet {
Dog as dog => println(dog.fetch())
default => println(pet.sound())
}
}
Interface or generics#
Use an interface when one list or parameter must accept different classes. Use generics when the caller's concrete type should be kept, and a tagged union when the set of types is closed and known.
