Skip to content
FutureBlitz

    Install

    Your own types, 6 of 8

    Interfaces

    Say what a type can do with an Interface, implement it in several Classes, and use std.core's own.

    What you’ll build

    A notification system that can send a message by email, by text message, or to a log, and treats each the same way. Then a Version type that sorts, through one of std.core’s Interfaces. An Interface lists Methods a type must have, without saying how they work.

    Declaring and implementing

    An Interface lists Methods by their first lines alone. A Class that says Implements the Interface must have each of them, with the same parameters and result, or it doesn’t compile.

    notify.fb
    Import std.core
    
    Interface Notifier
        Method Send(to: String, message: String)
    End Interface
    
    Class Email Implements Notifier
        Method Send(to: String, message: String)
            Print("email to {to}: {message}")
        End Method
    End Class
    
    Class TextMessage Implements Notifier
        Method Send(to: String, message: String)
            Print("text to {to}: {message}")
        End Method
    End Class
    
    Email().Send("ada", "Your order shipped.")
    TextMessage().Send("ada", "Your order shipped.")
    Output
    email to ada: Your order shipped.
    text to ada: Your order shipped.

    An Interface as a type

    An Interface is a type too. A variable or array of it holds objects of any Class that implements it, and a call through it runs the object’s own Method. Code written for Notifier works with every kind of notifier, including ones written later.

    notify.fb
    Import std.core
    
    Interface Notifier
        Method Send(to: String, message: String)
    End Interface
    
    Class Email Implements Notifier
        Method Send(to: String, message: String)
            Print("email to {to}: {message}")
        End Method
    End Class
    
    Class Log Implements Notifier
        Field lines: Int
    
        Method Send(to: String, message: String)
            lines++
            Print("log line {lines}: {to}: {message}")
        End Method
    End Class
    
    Function Broadcast(notifiers: Notifier[], message: String)
        For notifier In notifiers
            notifier.Send("ops", message)
        End For
    End Function
    
    Local log = Log()
    Broadcast([Email(), log], "disk 90% full")
    Broadcast([log], "backup done")
    Output
    email to ops: disk 90% full
    log line 1: ops: disk 90% full
    log line 2: ops: backup done

    A Struct can implement an Interface too, for code that’s generic over types, but a value of an Interface type is always an object, never a Struct.

    Default Methods

    A Method in an Interface can have a body: a default, which every type that implements the Interface gets unless it writes its own. Here every notifier can SendAll through its own Send.

    notify.fb
    Import std.core
    
    Interface Notifier
        Method Send(to: String, message: String)
    
        Method SendAll(people: String[], message: String)
            For person In people
                Send(person, message)
            End For
        End Method
    End Interface
    
    Class TextMessage Implements Notifier
        Method Send(to: String, message: String)
            Print("text to {to}: {message}")
        End Method
    End Class
    
    TextMessage().SendAll(["ada", "alan", "grace"], "The meeting moved to 3pm.")
    Output
    text to ada: The meeting moved to 3pm.
    text to alan: The meeting moved to 3pm.
    text to grace: The meeting moved to 3pm.

    std.core’s Interfaces

    The language works through Interfaces in std.core. A type that implements Printable, with a ToString Method, can be printed. One that implements Comparable, with an Operator <, gets >, <=, and >= from its defaults, and an array of it can Sort.

    versions.fb
    Import std.core
    Import std.collections
    
    Struct Version Implements Comparable, Printable
        Field Major: Int
        Field Minor: Int
    
        Operator <(a: Version, b: Version): Bool
            If a.Major != b.Major Then Return a.Major < b.Major
            Return a.Minor < b.Minor
        End Operator
    
        Method ToString(): String => "{Major}.{Minor}"
    End Struct
    
    Local versions = [Version(1, 10), Version(1, 2), Version(0, 9)]
    versions.Sort()
    Print(versions)
    Print(Version(1, 2) < Version(1, 10), Version(2, 0) >= Version(1, 9))
    Output
    [0.9, 1.2, 1.10]
    True True

    Comparing Major, then Minor, as integers puts 1.2 before 1.10, which sorting their text wouldn’t. Operators on your own types covers Operator declarations.

    What you learned

    • An Interface lists Methods; a type that Implements it must have each one.
    • A value of an Interface type holds any implementing Class’s object, and calls run its Methods.
    • A Method with a body in an Interface is a default, which types get unless they write their own.
    • Printable makes a type printable, and Comparable makes it compare and sort.

    Report an issue with this page or with FutureBlitz on GitHub.