8.12

4.3 Methods🔗ℹ

A method clause adds a method to a class or interface. Withn a class, the method can refer to fieds directly, or it can use this, which refers to the object whose method is called.

class Posn(x, y):

  method mdist():

    x + y // same as: this.x + this.y

  method move(dx :~ Int, dy :~ Int):

    if dx == 0 && dy == 0

    | this

    | Posn(x+dx, y+dy)

> Posn(1, 2).mdist()

3

> Posn(1, 2).move(0, 0)

Posn(1, 2)

> Posn(1, 2).move(10, -10)

Posn(11, -8)

In the same way that a class can be used as a namespace to refer to a field accessor like Posn.x or Posn.y, it can access a variant of a method that expects the this object as an extra initial argument.

> Posn.move(Posn(1, 2), 10, -10)

Posn(11, -8)

Methods are inherited in a subclass. Use the override modifier to override a method; attempting to replace a method with just method will report an error. To call the superclass implementation for a method that is overidden (usually in the overriding implementation), use super plus the . operator and the method name.

class Posn(x, y):

  nonfinal

  method mdist():

    x + y

  method area():

    0

class Posn3D(z):

  extends Posn

  override method mdist():

    super.mdist() + z

> def p = Posn3D(1, 2, 3)

> p

Posn3D(1, 2, 3)

> p.mdist()

6

> p.area()

0

Other method modifiers includes final to prevent overriding in subclasses and abstract (without a method body) to insist on overriding in a subclass before the subclass can be instantiated. As a shorthand, the form name method can be omitted just after override, final, or abstract.

Methods of an interface are typically abstract, and so the abstract modifier is implicit for method within interface if the method does not have a body. An interface can also supply implemented methods, and those implementations can refer to other methods, whether implemented, abstract, or inherited from a superinterface.

interface Shape:

  method area()

  method is_empty():

    area() == 0

class Square(side):

  implements Shape

  override area():

    side*side

> Square(0).is_empty()

#true

The declaration above of an area method in Shape specifies that the method should accept zero arguments, but that intent is not enforced on implementations. That is, a class might implement area to take additional arguments Result annotations are different. If area declares a result annotation, a check is added to each implementation to ensure that it results a satisfying result.

interface Shape:

  method area() :: Real

class Square(side):

  implements Shape

  override area():

    "downtown"

> Square(0).area()

area: result does not satisfy annotation

  result: "downtown"

  annotation: Real

This enforcement of result contracts applies to overridding in general, not just overiding to implement an abstract method. When an overriding method has its own result annotation, then both the overriding annotation and the inhereited annotation(s) apply to the method and any further overrides.