Class anatomy: properties, constructors, methods, override and default operators

The complete source of classes_anatomy.ek9, compiled by the EK9 compiler. Shown on classes.html, methods.html, concepts/class-anatomy.html.

#!ek9
defines module site.classes.anatomy

  defines class

    //A class is closed unless it says 'as open' - SavingsAccount below extends it.
    Account as open
      //Properties are always private; 'as T?' means "no value until a constructor gives it one".
      owner as String?
      balance as Integer?

      //The properties have no initial value, so a no-argument constructor must be private:
      //nothing outside the class can make an Account with no owner.
      default private Account()

      //A constructor's parameters go in one '->' block; 'this.x: x' copies each into its property.
      Account()
        ->
          owner as String
          balance as Integer
        this.owner: owner
        this.balance: balance

      //A method that changes the object.
      deposit()
        -> amount as Integer
        balance += amount

      //A method that returns a value: there is no 'return', the result is the '<-' variable.
      describe() as pure
        <- rtn as String: `${owner} holds ${balance}`

      //'?' (set when every property is set), '<=>' (sameness - '==', '<' and the rest are generated
      //from it), '#?' (goes with sameness) and '$' (the String form).
      default operator ?
      default operator <=>
      default operator #?
      default operator $

    SavingsAccount extends Account
      rate as Integer?

      SavingsAccount()
        ->
          owner as String
          balance as Integer
          rate as Integer
        super(owner, balance)
        this.rate: rate

      //'override' is required to replace a method of the super class; 'super.' calls the original.
      override describe() as pure
        <- rtn as String: `${super.describe()} at ${rate}%`

      //A sub class with its own properties gives its own '?', '$' and sameness (with '#?'):
      //Account's cannot know 'rate', so each of these calls Account's first, then adds it.
      default operator ?
      default operator $
      default operator <=>

  defines program

    ClassAnatomy()
      stdout <- Stdout()

      ann <- Account("Ann", 120)
      bob <- SavingsAccount("Bob", 80, 3)

      stdout.println(ann.describe())
      stdout.println(bob.describe())

      bob.deposit(40)
      stdout.println(bob.describe())

      stdout.println($ann)

      twin <- Account("Ann", 120)
      carl <- Account("Carl", 50)
      stdout.println(`ann == twin: ${ann == twin}`)
      stdout.println(`ann < carl: ${ann < carl}`)
      stdout.println(`ann is set: ${ann?}`)

Download classes_anatomy.ek9

Output

Ann holds 120
Bob holds 80 at 3%
Bob holds 120 at 3%
Account(owner=Ann, balance=120)
ann == twin: true
ann < carl: true
ann is set: true