Static properties and methods on interfaces

In both Gosu and Java, you can add static variables to types to declare data that exists at the type level. For Gosu classes, you add such data to a class by adding a declaration line with the static modifier:

public static var MyVariable

Adding a var declaration does not imply the ability to get or set a variable or property on an instance of the type. A var declaration on an interface defines a static constant variable on the type. A static constant variable is conceptually similar to a static variable in a Gosu class. However, for a variable on an interface, the static keyword is implicit and the value is read-only. The declaration does not specify the ability to get or set a variable or property on an instance of this type. Although you can use property getter and setter syntax to define data used with object instances, you do not use this syntax for static constant variables. This is why static constant variables do not require assignment compatibility between the interface and other types.

For example, the declaration of the Age variable in the following example will not compile because it implicitly creates a static constant variable, which must be initialized:

package examples.pl.gosu.intf

interface DemoInterface {
  property get Name() : String
  property set Name(n : String)
  public function count(s : String) : int

  public var Age : Integer  // Compilation error. A static constant variable requires initialization.
}

Initializing the value of the static constant variable clears the error:

package examples.pl.gosu.intf

interface DemoInterface {
  property get Name() : String
  property set Name(n : String)
  public function count(s : String) : int

  public var Age : Integer = 18  // OK because the static constant variable is initialized.
}

The following code defines a class that implements the DemoInterface interface:

uses examples.pl.gosu.intf.DemoInterface

class MyClass implements DemoInterface {
  var _name : String
  override property get Name() : String {
    return "name: " + _name
  }
  override property set Name(n:String) {
    _name = n
  }
  override function count(s : String) : int {
    return s.length()
  }
}

The following code creates an instance of the class and tests its properties:

var myObject = new MyClass()
myObject.Name = "Andy Applegate"
print(myObject.Name)
print(myObject.count(myObject.Name))
print(myObject.Age)

Output:

name: Andy Applegate
20
18
Note: You can add the static modifier in the declaration of the age variable, but in interface declarations this modifier is implicit and therefore redundant.

To call a static interface method, do so using the interface name directly, rather than the name of the implementing class. This is required because static interface methods are not inherited.

package examples.pl.gosu.intf

interface DemoInterface {
  property get Name() : String
  property set Name(n : String)
  public function count(s : String) : int

  public var Age : Integer  = 18

  static function nameWithLabel( person: String ) : String {
    return "Person: " + person
  }
}

The following code defines a class that implements the modified DemoInterface interface:

uses examples.pl.gosu.intf.DemoInterface

class MyClass implements DemoInterface {
  var _name : String
  override property get Name() : String {
    return "name: " + _name
  }
  override property set Name(n:String) {
    _name = n
  }
  override function count(s : String) : int {
    return s.length()
  }
}

The following code creates and tests an instance of the class, calling the DemoInterface.nameWithLabel static interface method:

var myObject = new MyClass()
myObject.Name = "Andy Applegate"
print(myObject.Name)
// print(MyClass.nameWithLabel(myObject.Name))       // error: must use method directly on interface
print(DemoInterface.nameWithLabel( myObject.Name ))  // OK

Output:

name: Andy Applegate
Person: name: Andy Applegate

Another reason static interface methods must be called directly from their interfaces is to prevent conflicts when a class implements multiple interfaces having the same static method names. In this example, a second interface, DemoInterface2, has an identically named static method to the one in DemoInterface:

interface DemoInterface2 {
  static function nameWithLabel( person: String ) : String {
    return "Person2: " + person
  }
}

MyClass is modified here to implement both interfaces:

class MyClass implements DemoInterface, DemoInterface2 {
  var _name : String
  override property get Name() : String {
    return "name: " + _name
  }
  override property set Name(n:String) {
    _name = n
  }
  override function count(s : String) : int {
    return s.length()
  }
}

The following code creates an instance of MyClass, calling the static methods defined on both interfaces:

var myObject = new MyClass()
myObject.Name = "Andy Applegate"
print(myObject.Name)
print(DemoInterface.nameWithLabel( myObject.Name ))    // ok
print(DemoInterface2.nameWithLabel( myObject.Name ))    // ok

Output:

name: Andy Applegate
Person: name: Andy Applegate
Person2: name: Andy Applegate

In this last example, both interfaces can be implemented without conflict because their static methods are not inherited.