Downcasting

Downcasting is the process of determining if an object is of a particular subclass type. If the object has that subtype (either the object is of that subtype, or is a subtype of it), a reference to the subobject is returned, otherwise Null is returned. In either case, the type of the resulting value is compatible with the named subclass type.

This downcast inquiry into the actual type of an object can be used when the object has been passed to some general facility, then passed back to more specific code. For example, the general facility might be a dispatch mechanism, or a new storage structure such as a binary balanced tree. These general facilities won't know about specific types, but it might be necessary for the specific program using the general facility to recover the actual type of an object that is returned to it by the general facility.

A downcast should not be used when you could add members to a common base class to provide the same functionality. In other words, if you find yourself using the downcast to test whether the object is one or another of several related types, you should add members to the common base class to provide the operations or properties that you are trying to access, then provide separate implementations of those actions or properties in each specific class.

If the downcast operator is to be used in the context of further object expressions (such as references to other members using dot notation) then the downcast must be enclosed in parentheses to emphasize that the downcast happens before the member reference.

class Fruit
   property number FruitCount;
end-class;

class Banana extends Fruit
   property number BananaCount;
end-class;

In the following code example, first the variables are assigned. All the variable assignments are legal, for Banana is a subtype of Fruit.

local Banana &MyBanana = Create Banana();
local Fruit &MyFruit = &MyBanana;  /* okay, Banana is a subtype of Fruit */
local number &Num = &MyBanana.BananaCount;

The next two lines of code don't produce an error, as &MyFruit is currently a Banana at runtime.

&MyBanana = &MyFruit As Banana;  
&Num = (&MyFruit As Banana).BananaCount; /* same as &MyBanana.BananaCount */

In the next lines of code, we're reassigning &MyFruit to be of type Fruit, that is, of the superclass Fruit. When you try to reassign &MyFruit as Banana, &MyBanana is assigned as Null, because &MyFruit is no longer of type Banana.

&MyFruit = Create Fruit();

&MyBanana = &MyFruit As Banana; /* assigns Null - &MyFruit isn't a Banana */