Skip to content
FutureBlitz

    Install

    Your own types, 4 of 8

    Inheritance

    Extend a Class, override its Methods, call the original with Super, and test an object's Class.

    What you’ll build

    A program that keeps circles, rectangles, and squares in one list of shapes, and asks each for its area. Each shape works its area out its own way, while the code that uses them treats them all as Shapes.

    Extending a Class

    Class Circle Extends Shape makes Circle a kind of Shape: it has Shape’s fields and Methods, and adds its own. Its New calls Super.New(...), the New of the Class it extends, to set up the Shape part. An Abstract Class can’t be made itself; it exists to be extended, and its Abstract Methods are ones each Class extending it must give.

    shapes.fb
    Import std.core
    
    Abstract Class Shape
        ReadOnly Field Name: String
    
        New(name: String)
            Name = name
        End New
    
        Abstract Method Area(): Float
    
        Method Describe(): String => "{Name} with area {Area():.2}"
    End Class
    
    Class Rectangle Extends Shape
        Field width: Float
        Field height: Float
    
        New(width: Float, height: Float)
            Super.New("rectangle")
            Self.width = width
            Self.height = height
        End New
    
        Override Method Area(): Float => width * height
    End Class
    
    Local box = Rectangle(2.0, 3.0)
    Print(box.Describe())
    Output
    rectangle with area 6.00

    Override is required on a Method that replaces one the Class inherits, so replacing a Method is never an accident.

    One list, many Classes

    An object of a Class converts to any Class it extends, so a Shape[] holds circles and rectangles alike. A call to a Method runs the version of the object’s own Class: Describe, which Shape wrote, calls whichever Area the object has.

    shapes.fb
    Import std.core
    Import std.math
    
    Abstract Class Shape
        ReadOnly Field Name: String
    
        New(name: String)
            Name = name
        End New
    
        Abstract Method Area(): Float
    
        Method Describe(): String => "{Name} with area {Area():.2}"
    End Class
    
    Class Circle Extends Shape
        Field radius: Float
    
        New(radius: Float)
            Super.New("circle")
            Self.radius = radius
        End New
    
        Override Method Area(): Float => Pi * radius * radius
    End Class
    
    Class Rectangle Extends Shape
        Field width: Float
        Field height: Float
    
        New(width: Float, height: Float, name: String = "rectangle")
            Super.New(name)
            Self.width = width
            Self.height = height
        End New
    
        Override Method Area(): Float => width * height
    End Class
    
    Local shapes: Shape[] = [Circle(1.0), Rectangle(2.0, 3.0), Circle(0.5)]
    Local total: Float = 0.0
    For shape In shapes
        Print(shape.Describe())
        total += shape.Area()
    End For
    Print("total area {total:.2}")
    Output
    circle with area 3.14
    rectangle with area 6.00
    circle with area 0.79
    total area 9.93

    Pi is a constant from std.math.

    Super

    Inside an override, Super.Method() calls the version of the Class extended, so an override can add to it instead of starting over. Here Square extends Rectangle and adds to its Describe.

    shapes.fb
    Import std.core
    
    Class Rectangle
        Field width: Float
        Field height: Float
    
        New(width: Float, height: Float)
            Self.width = width
            Self.height = height
        End New
    
        Method Area(): Float => width * height
        Method Describe(): String => "{width} by {height}, area {Area()}"
    End Class
    
    Class Square Extends Rectangle
        New(side: Float)
            Super.New(side, side)
        End New
    
        Override Method Describe(): String => "a square, " + Super.Describe()
    End Class
    
    Local shapes: Rectangle[] = [Rectangle(2.0, 3.0), Square(4.0)]
    For shape In shapes
        Print(shape.Describe())
    End For
    Output
    2 by 3, area 6
    a square, 4 by 4, area 16

    A Final Method can’t be overridden, and a Final Class can’t be extended.

    Asking an object’s Class

    x Is T tells whether an object is of Class T, or of one extending it. x As? T gives the object as a T, or Null when it isn’t one, and Select with Case name As T picks a case by Class. x As T converts without asking, and panics when the object isn’t a T.

    shapes.fb
    Import std.core
    
    Class Shape
    End Class
    
    Class Circle Extends Shape
        Field Radius: Float = 1.0
    End Class
    
    Class Rectangle Extends Shape
    End Class
    
    Class Square Extends Rectangle
    End Class
    
    Local shapes: Shape[] = [Circle(), Rectangle(), Square()]
    For shape In shapes
        Local circle = shape As? Circle
        If circle != Null Then Print("a circle of radius {circle.Radius}")
        If shape Is Rectangle Then Print("a rectangle, or a kind of one")
    
        Select shape
            Case square As Square
                Print("  exactly: a square")
            Case rectangle As Rectangle
                Print("  exactly: a rectangle")
            Default
                Print("  exactly: neither")
        End Select
    End For
    Output
    a circle of radius 1
      exactly: neither
    a rectangle, or a kind of one
      exactly: a rectangle
    a rectangle, or a kind of one
      exactly: a square

    The Case for Square comes before the one for Rectangle, since a Square is a Rectangle too: the other way round, the Square case could never match, and fb says so.

    What you learned

    • Extends makes a Class a kind of another, with its fields and Methods.
    • Super.New(...) runs the extended Class’s constructor, and Super.M() its Method.
    • Override replaces an inherited Method; calls run the object’s own Class’s version.
    • An Abstract Class can’t be made, and each Abstract Method must be overridden.
    • Is, As?, As, and Case x As T test and convert an object’s Class.

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