BBj Custom Objects Tutorial: Program #7 - Constructors and Field Initialization

Example Programs

Here are some example BBj programs. Each example is designed to demonstrate some aspect of using Custom Object Classes in BBj.

The complete source code for each program is included in an appropriately named sub-folder in the zip file you can download here.

Program #7: Constructors and Field Initialization

Let’s take a look at another BBj program—one that demonstrates some interesting facts about the order in which constructors execute and fields are initialized. First, in Vehicle.bbj, we create a basic Vehicle class to act as the top level superclass for all of our vehicle types. Then, in ClassicCar.bbj, EnhancedCar.bbj, and RaceCar.bbj, we create classes that extend the Vehicle class, with code that overrides the superclass constructor behavior. Finally, we create the CompareVehicles.bbj code to use the various Vehicle classes and print out information to make it clear how execution proceeds. Again, we show the code for each new source file using a different border color for readability:

REM /**
REM  * Vehicle.bbj
REM  * This file holds the Vehicle custom object class
REM  */

REM ===============================================================
REM The Vehicle Class, which defines the most basic type of
REM    vehicle possible.  Vehicle will be used as a superclass
REM    for a number of other classes.
REM ===============================================================
CLASS public Vehicle
    FIELD public BBjString Color$ = "BLACK"
    FIELD protected BBjNumber MaxSpeed = 18
    FIELD public BBjString Description$ = "S/N:" +
:         str(#SerialNumber) + ", Color: " + #Color$ +
:         ", MaxSpeed: " + str(#MaxSpeed)
    FIELD protected static BBjNumber SerialNumber = 0

    REM ===============================================================
    REM Constructors

    REM This public default constructor can be called by anyone
    METHOD public Vehicle()
        PRINT "= In Vehicle constructor: " + #getDescription()
    METHODEND

    REM ===============================================================
    REM Field accessor methods

    REM Since #MaxSpeed is protected, we need to explicitly define a
    REM   get method in order to have public access
    METHOD public BBjNumber getMaxSpeed()
        METHODRET #MaxSpeed
    METHODEND
CLASSEND
REM /**
REM  * ClassicCar.bbj
REM  * This FILE defines the ClassicCar custom object class
REM  *   which extends the Vehicle class
REM  */
USE ::Vehicle.bbj::Vehicle

REM ===============================================================
REM The ClassicCar class, which extends the Vehicle class.
REM    The ClassicCar class will be a superclass for other classes,
REM    and itself extends the Vehicle class.
REM ===============================================================
CLASS public ClassicCar extends Vehicle
    FIELD public BBjNumber MilesPerGallon = 15

    REM ===============================================================
    REM Constructors

    REM This PRIVATE default constructor cannot be called directly -
    REM   not from subclasses, and not from external code.
    METHOD private ClassicCar()
    METHODEND

    REM This public constructor can be called from subclasses or from
    REM   external code
    METHOD public ClassicCar(BBjNumber maxSpeed)
        PRINT "=== In ClassicCar constructor: ", #getDescription()
        #setSerialNumber(#getSerialNumber() + 1)
        #setMaxSpeed(maxSpeed)
    METHODEND
CLASSEND
REM /**
REM  * EnhancedCar.bbj
REM  * This file defines the EnhancedCar custom object class
REM  *   which extends the ClassicCar class
REM  */
USE ::ClassicCar.bbj::ClassicCar

REM ===============================================================
REM The EnhancedCar class, which extends the ClassicCar class.
REM    The EnhancedCar class will be a superclass for other classes,
REM    and itself extends the ClassicCar class.
REM ===============================================================
CLASS public EnhancedCar extends ClassicCar
    REM ===============================================================
    REM Constructors

    REM This PRIVATE default constructor cannot be called directly -
    REM   not from subclasses, and not from external code.
    METHOD private EnhancedCar()
    METHODEND

    REM This public constructor can be called from subclasses or from
    REM   external code
    METHOD public EnhancedCar(BBjNumber maxSpeed)
        #super!(maxSpeed)

        PRINT "===== In EnhancedCar constructor: " + #getDescription()
    METHODEND

    REM This public constructor can be called from subclasses or from
    REM   external code
    METHOD public EnhancedCar(BBjNumber maxSpeed,
:                             BBjNumber milesPerGallon)
        REM Chain to the one-argument constructor
        #this!(maxSpeed)

        PRINT "===== In EnhancedCar constructor: " + #getDescription()
        PRINT "------- Legacy description: ", #getDescription()
        #setMilesPerGallon(milesPerGallon)
        #setDescription(#newDescription())
        PRINT "------- New description: ", #getDescription()
    METHODEND

    REM ===============================================================
    REM This protected method is only visible to this class and any of
    REM   its subclasses.  Get a new description for this subclass.
    METHOD protected BBjString newDescription()
        a$ = "S/N: " + str(#getSerialNumber()) +
:            ", shiny " + #getColor() +
:            " AND gets " + str(#getMilesPerGallon()) +
:            " miles per gallon"
        METHODRET a$
    METHODEND
CLASSEND
REM /**
REM  * RaceCar.bbj
REM  * This file defines the RaceCar custom object class
REM  *   which extends the EnhancedCar class
REM  */
USE ::EnhancedCar.bbj::EnhancedCar

REM ===============================================================
REM The RaceCar class, which extends the EnhancedCar class.
REM ===============================================================
CLASS public RaceCar extends EnhancedCar
    REM ===============================================================
    REM Constructors

    REM This public constructor can be called by anyone, as could the
    REM   implicit default constructor that BBj makes available
    METHOD public RaceCar(BBjNumber maxSpeed, BBjNumber milesPerGallon)
        #super!(maxSpeed)

        PRINT "======= In RaceCar constructor: " + #getDescription()

        #setMilesPerGallon(milesPerGallon)
        #setColor("RED")
        PRINT "--------- Legacy description: ", #getDescription()
        #setDescription(#newDescription())
        PRINT "--------- New description: ", #getDescription()
    METHODEND
CLASSEND
REM /**
REM  * CompareVehicles.bbj
REM  * This code demonstrates/uses Custom Object Classes with
REM  *   advanced/complicated constructor code
REM  */
USE ::Vehicle.bbj::Vehicle
USE ::ClassicCar.bbj::ClassicCar
USE ::EnhancedCar.bbj::EnhancedCar
USE ::RaceCar.bbj::RaceCar

REM Construct and use a Vehicle
DECLARE Vehicle transport!
transport! = new Vehicle()
PRINT "++ Basic Vehicle Created: ", transport!.getDescription()
PRINT "########################################################"

REM Construct and use a ClassicCar, which extends a Vehicle
setErr aLabel
DECLARE ClassicCar cCar!
cCar! = new ClassicCar()

aLabel:
  print "***** WARNING: The ClassicCar() constructor is private"
  seterr 0
  cCar! = new ClassicCar(45)
  PRINT "++++ ClassicCar Created: ", cCar!.getDescription()
  PRINT "########################################################"

REM Construct and use an EnhancedCar, which extends a ClassicCar
DECLARE EnhancedCar eCar!
eCar! = new EnhancedCar(95, 48)
PRINT "++++++ EnhancedCar Created: ", eCar!.getDescription()
PRINT "########################################################"

REM Construct and use a RaceCar, which extends an EnhancedCar
DECLARE RaceCar rCar!
rCar! = new RaceCar(217, 3.2)
PRINT "++++++++ RaceCar Created: ", rCar!.getDescription()
PRINT "########################################################"

Execution

Creating the BBj source files and then running Program 7 (the code in CompareVehicles.bbj) resulted in the following SysConsole output:

Figure 8. The Output from Program 7

Observations

Here are some details that this code demonstrates:

  • In the CompareVehicles.bbj statement:

    transport! = new Vehicle()

    We are invoking the default no-argument constructor for the Vehicle class. This constructor is defined in Vehicle.bbj to print out a line each time it is invoked. This permits you to see (in Figure 8) that Vehicle’s default constructor is being called first each time a Vehicle, or any of its subclasses, are instantiated.

  • Within each class, static fields are initialized in the order in which they appear (from top to bottom)

    • All static fields are initialized before any instance of the class is created.

    • This must be true in order for static field values to be available for use without needing to instantiate an instance of the class.

    • Static fields can have their values changed at any time, either by code in an instance of the class or by code calling static methods on the class.

    • Because #SerialNumber is a static field of the Vehicle class, it is initialized to 0 at the start of the BBj session. Thus the Vehicle class constructors have access to that initialized value regardless of the order of the #SerialNumber static field declaration in the code.

    • If a class’s static fields change values over time, then each instance of that class will use whatever values are set at instantiation time in its constructors.

  • Within each class, the non-static field initializers are executed in the order in which they appear. So when Vehicle.Description$ is initialized, it can (and does) use the already initialized values of the fields Vehicle.Color$ and Vehicle.MaxSpeed.

  • The default no-argument constructor for ClassicCar has been overridden and declared private. CompareVehicles.bbj’s statement: cCar! = new ClassicCar() uses that constructor, resulting in an error and this output in Figure 8:

    ***** WARNING: The ClassicCar() constructor is private

  • The constructor ClassicCar(BBjNumber maxSpeed) sets the values of the #SerialNumber and #MaxSpeed fields. Since the constructor is executed after the field initializers, the values reported in the description by the constructor will continue to reflect the earlier values until code updates #Description$.

  • The constructor EnhancedCar(BBjNumber maxSpeed) explicitly calls a specific superclass constructor. It passes the new maxSpeed value to the superclass constructor using the syntax:

    #super!(maxSpeed)

  • The constructor EnhancedCar(BBjNumber maxSpeed, BBjNumber milesPerGallon) calls a different constructor of EnhancedCar using the syntax:

    #this!(maxSpeed)

    This is what is referred to as chaining constructors (see Order of Execution for more information on the impact of chaining).

  • A constructor can call methods of the class being constructed. So, for example, the constructor EnhancedCar(BBjNumber maxSpeed, BBjNumber milesPerGallon) contains the statement #setDescription(#newDescription()). When the constructor is complete, the value of #Description$ will contain the value returned by #newDescription().

BBj Custom Objects Tutorial Contents

BBj Custom Objects Tutorial: Introduction

BBj Custom Objects Tutorial: Interfaces

BBj Custom Objects Tutorial: Classes

BBj Custom Objects Tutorial: Fields

BBj Custom Objects Tutorial: Methods

BBj Custom Objects Tutorial: Using Custom Objects

BBj Custom Objects Tutorial: Program #1 - Writing a Check

BBj Custom Objects Tutorial: Program #2 - Protected and Private Fields

BBj Custom Objects Tutorial: Program #3: The Static Keyword

BBj Custom Objects Tutorial: Program #4 - Error Handling

BBj Custom Objects Tutorial: Program #5 - Class Inheritance

BBj Custom Objects Tutorial: Program #6 - Callback Choices

BBj Custom Objects Tutorial: Program #7 - Constructors and Field Initialization