Object variables: Difference between revisions
(38 intermediate revisions by 6 users not shown) | |||
Line 1: | Line 1: | ||
== | An object variable is a proxy or substitute for an object. You do not access or perform actions against an object directly but instead via operations on an object variable that references the object. This page discusses how you | ||
create, work with, and discard object variables. | |||
==Why use an object reference instead of an object== | |||
Object variables are used to declare variables that refer to instances of | Object variables are used to declare variables that refer to instances of | ||
a class. | a class. | ||
Despite their name, they are not really objects, | Despite their name, they are not really objects, as such, | ||
but references or pointers to objects. | but references or pointers to objects. | ||
The underlying objects themselves are never directly accessible and can only | The underlying objects themselves are never directly accessible and can only | ||
Line 23: | Line 25: | ||
The following is an example of assignment of an object reference, rather | The following is an example of assignment of an object reference, rather | ||
than an object: | than an object: | ||
< | |||
class pet | <p class="code">class pet | ||
public | public | ||
variable type is string len 16 | variable type is string len 16 | ||
Line 37: | Line 39: | ||
%fluffy:type = 'Cat' | %fluffy:type = 'Cat' | ||
print %rover:type | print %rover:type | ||
</ | </p> | ||
Because the statement | |||
Because the statement <code>%fluffy = %rover</code> assigns to %fluffy a reference | |||
to the same object as %rover, both variables end up referring | to the same object as %rover, both variables end up referring | ||
to the same object. | to the same object. | ||
The statement | The statement <code>%fluffy:type = 'Cat'</code> therefore changes | ||
the underlying object pointed to by %rover (since it's the same object | the underlying object pointed to by %rover (since it's the same object | ||
as the one pointed to by %fluffy). | as the one pointed to by %fluffy). | ||
As a result, the Print statement prints | As a result, the Print statement prints <code>Cat</code>. | ||
As assignment assigns references, comparison compares references. | As assignment assigns references, comparison compares references. | ||
Line 52: | Line 55: | ||
Consider the following example:: | Consider the following example:: | ||
< | <p class="code">class pet | ||
class pet | |||
public | public | ||
variable type is string len 16 | variable type is string len 16 | ||
Line 67: | Line 69: | ||
%fluffy:type = 'Dog' | %fluffy:type = 'Dog' | ||
if %fluffy eq %rover then | if %fluffy eq %rover then | ||
print 'They | print 'They''re equal!' | ||
end if | end if | ||
</ | </p> | ||
This will not print | |||
This will not print <code>They're equal</code>. | |||
Even though the objects | Even though the objects | ||
pointed to by %fluffy and %rover have the same variable values, they | pointed to by %fluffy and %rover have the same variable values, they | ||
are different objects, so %fluffy is not equal to %rover. | are different objects, so %fluffy is not equal to %rover. | ||
However, if the code were changed: | However, if the code were changed: | ||
< | |||
class pet | <p class="code">class pet | ||
public | public | ||
variable type is string len 16 | variable type is string len 16 | ||
Line 88: | Line 91: | ||
%rover = %fluffy | %rover = %fluffy | ||
if %fluffy eq %rover then | if %fluffy eq %rover then | ||
print 'They | print 'They''re equal!' | ||
end if | end if | ||
</ | </p> | ||
sets %rover to point to the same object as %fluffy, making them equal. | <code>They're equal</code> will be printed, because <code>%rover = %fluffy</code> | ||
: | sets <code>%rover</code> to point to the same object as <code>%fluffy</code>, making them equal. | ||
<p class="note>'''Note:''' | |||
Since order comparisons (GE, LE, GT, and LT) are meaningless for references — | Since order comparisons (GE, LE, GT, and LT) are meaningless for references — | ||
when is a reference to one object greater than a reference to another? — | when is a reference to one object greater than a reference to another? — | ||
only equality and inequality (EQ and NE) comparisons are allowed for objects. | only equality and inequality (EQ and NE) comparisons are allowed for objects. </p> | ||
Despite the foregoing remarks, for most purposes, one can still think of | Despite the foregoing remarks, for most purposes, one can still think of | ||
Line 103: | Line 107: | ||
the objects themselves, rather than references to the object. | the objects themselves, rather than references to the object. | ||
For example, in the statement: | For example, in the statement: | ||
< | |||
%rover:type = 'Ferret' | <p class="code">%rover:type = 'Ferret' | ||
</ | </p> | ||
it is quite natural to think of the statement as setting %rover's Type to | it is quite natural to think of the statement as setting %rover's Type to | ||
"ferret," rather than as setting the type of the object referenced | "ferret," rather than as setting the type of the object referenced | ||
by %rover to "Ferret." | by %rover to "Ferret." | ||
==Creating object instances== | |||
Since object variables are references to objects, two questions naturally | Since object variables are references to objects, two questions naturally | ||
arise: what object does an object variable refer to initially, and | arise: what object does an object variable refer to initially, and | ||
Line 117: | Line 122: | ||
points to ''no'' object. | points to ''no'' object. | ||
So, the following program would cause a request cancellation error: | So, the following program would cause a request cancellation error: | ||
< | |||
class pet | <p class="code">class pet | ||
public | public | ||
variable type is string len 16 | variable type is string len 16 | ||
Line 127: | Line 132: | ||
%rover:type = 'Dog' | %rover:type = 'Dog' | ||
</ | </p> | ||
The statement | |||
The statement <code>%rover:type = 'Dog'</code> attempts to set the object's | |||
type to "Dog," but there is no instance of an object for | type to "Dog," but there is no instance of an object for | ||
which to set the type. | which to set the type. | ||
Line 136: | Line 142: | ||
So how, then, is an object created? | So how, then, is an object created? | ||
The answer is via a <var>Constructor</var>, usually the default one named <var>New</var>. | The answer is via a <var>Constructor</var>, usually the default one named <var>New</var>. | ||
===Using New or other Constructors=== | |||
<var> | When you invoke a <var>Constructor</var> method, <var class="product">SOUL</var> creates an object instance which is available to the <var>Constructor</var> code. The <var>Constructor</var>: | ||
<ul> | <ul> | ||
<li> | <li>Operates on the new instance (which is the value of <code>%this</code> within the <var>Constructor</var>) | ||
<p>Unlike a shared method, a <var>Constructor</var> operates on an object instance.</p> | |||
<li> | |||
<li>Returns the value of <code>%this</code> as the result. | |||
like a <var>Function</var>, | <p> | ||
Like a <var>Subroutine</var>, the <var>Constructor</var>'s <var>Return</var> statement may not specify a value, but like a <var>Function</var>, the <var>Constructor</var> does produce a result.</p> | |||
</ul> | </ul> | ||
< | In any non-<var>[[Dynamic dispatch#Overridable versus Abstract methods|Abstract]]</var> class, you can invoke a default <var>Constructor</var>, named <var>New</var>, which operates no further on <code>%this</code> and simply returns a new instance for explicit or implicit assignment. As described below, you can explicitly define <var>New</var> to operate on the new instance, you can define other constructors in addition to <var>New</var>, and you can also [[#Private constructors|"disallow"]] <var>New</var> from use outside the class definition. | ||
% | |||
</ | ====Syntax for Constructors==== | ||
< | |||
</ | |||
The syntax for invoking a <var>Constructor</var> is the same as the syntax for | The syntax for invoking a <var>Constructor</var> is the same as the syntax for | ||
invoking a <var>Shared</var> method which returns an instance of the class. | invoking a <var>Shared</var> method which returns an instance of the class. | ||
To invoke the <var>New</var> constructor and assign its result: | |||
<p class="code">%rover = new | |||
</p> | |||
You can explicitly indicate the class of the object as follows: | |||
<p class="code">%rover = %(pet):new | |||
</p> | |||
While specifying the class in this way is unnecessary and might | While specifying the class in this way is unnecessary and might | ||
be viewed as detrimental because it results in the class name | be viewed as detrimental because it results in the class name | ||
Line 163: | Line 173: | ||
clear in the part of code where the object is created. | clear in the part of code where the object is created. | ||
====Customizing a Constructor==== | |||
Sometimes it is useful to run code when creating an instance of a class. | Sometimes it is useful to run code when creating an instance of a class. | ||
In such cases, | In such cases, you can explicitly define the <var>New</var> <var>Constructor</var>: | ||
< | <p class="code">class pet | ||
class pet | |||
public | public | ||
variable initTime is float | variable initTime is float | ||
Line 175: | Line 185: | ||
end constructor | end constructor | ||
end class | end class | ||
</ | </p> | ||
In the above example, the class variable | In the above example, the class variable <code>initTime</code> is set | ||
to the time the instance of the object is created. | to the time the instance of the object is created. | ||
That code runs when the <var>New</var> <var>Constructor</var> is invoked in a program to create a <code>pet</code> instance: | |||
< | <p class="code">%josh is object pet | ||
%josh is object pet | |||
... | ... | ||
%josh = new | %josh = new | ||
</ | </p> | ||
Sometimes it is useful to have a constructor take parameters. | Sometimes it is useful to have a constructor take parameters. | ||
For example, if it doesn't make sense to have a Pet object without | For example, if it doesn't make sense to have a Pet object without | ||
a Type, make the New constructor require an input parameter that | a Type, make the <var>New</var> constructor require an input parameter that | ||
sets the class Type variable: | sets the class Type variable: | ||
< | <p class="code">class pet | ||
class pet | |||
public | public | ||
variable type is string len 16 | variable type is string len 16 | ||
Line 200: | Line 208: | ||
end constructor | end constructor | ||
end class | end class | ||
</ | </p> | ||
The parameters | The parameters | ||
must be specified on the New constructor: | must be specified on the <var>New</var> constructor: | ||
< | <p class="code">%snoopy is object pet | ||
%snoopy is object pet | |||
... | ... | ||
%snoopy = new('Beagle') | %snoopy = new('Beagle') | ||
</ | </p> | ||
As of <var class="product">[[Sirius Mods]]</var> Version 7.2, | As of <var class="product">[[Sirius Mods]]</var> Version 7.2, | ||
constructors are allowed to change the object being constructed. | constructors are allowed to change the object being constructed. | ||
For example, variable ExistingObj assigns | For example, variable <code>ExistingObj</code> assigns | ||
an existing Dachsie object when constructor Newbie is called: | an existing <code>Dachsie</code> object when constructor <code>Newbie</code> is called: | ||
< | <p class="code">class dachsie | ||
class dachsie | |||
public | public | ||
... | ... | ||
Line 231: | Line 237: | ||
... | ... | ||
end class | end class | ||
</ | </p> | ||
However, this feature was actually provided for a different purpose, | However, this feature was actually provided for a different purpose, | ||
and using it in the context shown above requires caution. | and using it in the context shown above requires caution. | ||
While | While <code>%this</code> is an input parameter to the <var>Constructor</var>, changing it changes the | ||
object returned to the | object returned to the <var>Constructor</var> invoker, so it acts almost like an output parameter. | ||
Modifying %this within the | Modifying <code>%this</code> within the <var>Constructor</var> can also produce unexpected behavior | ||
if the | if the <var>Constructor</var> is itself called from an [[Inheritance and polymorphism|extension class Construct statement]]. | ||
Instead of using a | Instead of using a <var>Constructor</var> to return a modified object as in the example | ||
above, you should consider using a [[#Virtual Constructor methods|"factory method"]]. | above, you should consider using a [[#Virtual Constructor methods|"factory method"]]. | ||
====Other ways to use a Constructor==== | |||
A <var>Constructor</var> can also be used in these ways: | |||
<ul> | <ul> | ||
<li>To specify a new instance of a class | <li>To specify a new instance of a class as an input parameter to a method: | ||
as an input parameter to a method: | <p class="code">class pet | ||
< | |||
class pet | |||
public | public | ||
... | ... | ||
Line 260: | Line 265: | ||
... | ... | ||
%lassie:compare( new('Collie') ) | %lassie:compare( new('Collie') ) | ||
</ | </p> | ||
<li>Without being explicitly specified, | <li>Without being explicitly specified, | ||
to automatically create an instance of an object | to automatically create an instance of an object | ||
when an object variable is first referenced. | when an object variable is first referenced. | ||
For such instantiation, you must | For such instantiation, you must | ||
specify [[Classes and | specify <var>[[Classes and Objects#The Auto keyword on object declarations|Auto New]]</var> in the object's declaration. | ||
</ul> | </ul> | ||
====Using multiple Constructors==== | |||
Most object-oriented languages support only a single constructor name for a class. | Most object-oriented languages support only a single constructor name for a class. | ||
If multiple constructors are needed, this is achieved with overloading — | If multiple constructors are needed, this is achieved with overloading — | ||
Line 276: | Line 282: | ||
parameter list. | parameter list. | ||
<var class="product">SOUL</var> supports '''multiple constructors''': | |||
< | <p class="code">class resource | ||
class resource | |||
public | public | ||
constructor newFromUrl(%url is string len 255) | constructor newFromUrl(%url is string len 255) | ||
Line 294: | Line 299: | ||
... | ... | ||
%res = new | %res = new | ||
</ | </p> | ||
This example illustrates: | This example illustrates: | ||
<ul> | <ul> | ||
<li>Constructors not called New are invoked in exactly the same way | <li>Constructors not called New are invoked in exactly the same way | ||
as the New constructor | as the <var>New</var> constructor. | ||
<p> | |||
A function that invokes a constructor can be used in any other | A function that invokes a constructor can be used in any other | ||
context in which the <var>New</var> <var>Constructor</var> can be used. | context in which the <var>New</var> <var>Constructor</var> can be used.</p> | ||
<li>It is recommended that you always begin | <li>It is recommended that you always begin <var>Constructor</var> names with the | ||
word "New" to distinguish them from other methods. | word "New" to distinguish them from other methods. | ||
</ul> | </ul> | ||
====Private Constructors==== | |||
Although this section has discussed only public constructors, that is, | Although this section has discussed only public constructors, that is, | ||
constructors defined for use outside the class definition, | constructors defined for use outside the class definition, | ||
you may also define '''private constructors''', | you may also define '''private constructors''', | ||
which may only be invoked from inside the class. | which may only be invoked from inside the class. | ||
You declare such a | You declare such a <var>Constructor</var> in the <var>Private</var> block of a class definition, | ||
and you define it with your other methods after the declaration blocks. | and you define it with your other methods after the declaration blocks. | ||
If you want to define a private constructor named New, however, | If you want to define a private constructor named New, however, | ||
you must make sure to specify | you must make sure to specify <code>Disallow New</code> | ||
in the Public block of the class definition (see | in the <var>Public</var> block of the class definition (see [[Classes and Objects#Declaration block syntax|"Declaration block | ||
No other private constructors require an additional Disallow New in the | syntax"]]). | ||
Public block. | No other private constructors require an additional <var>Disallow New</var> in the | ||
<var>Public</var> block. | |||
===<b id="Virtual Constructor methods"></b>Virtual constructors=== | |||
It is possible to have a non-constructor shared function return an | It is possible to have a non-constructor shared function return an | ||
instance of a class: | instance of a class: | ||
< | |||
class resource | <p class="code">class resource | ||
public shared | public shared | ||
function newFromUrl(%url is string len 255) - | function newFromUrl(%url is string len 255) - | ||
Line 349: | Line 350: | ||
... | ... | ||
%res = %res:newFromUrl('http://nfl.com/score') | %res = %res:newFromUrl('http://nfl.com/score') | ||
</ | </p> | ||
Such functions are often called '''factory methods''' or | Such functions are often called '''factory methods''' or | ||
virtual constructors. | virtual constructors. | ||
Line 358: | Line 360: | ||
<li>They might not actually instantiate an object but return a reference to an existing instance. Such a method may not truly be a factory method. | <li>They might not actually instantiate an object but return a reference to an existing instance. Such a method may not truly be a factory method. | ||
</ul> | </ul> | ||
The support for multiple constructors means that | The support for multiple constructors means that <var class="product">SOUL</var> | ||
users need factory methods less than those using | users need factory methods less than those using | ||
most other object-oriented programming languages. | most other object-oriented programming languages. | ||
Line 364: | Line 366: | ||
Virtual constructors can be invoked in much the same way that regular constructors are invoked, that is, without specifying the class name if the class name can be determined from context. | Virtual constructors can be invoked in much the same way that regular constructors are invoked, that is, without specifying the class name if the class name can be determined from context. | ||
For example, the above example of the NewFromUrl virtual constructor can be changed to the following: | For example, the above example of the NewFromUrl virtual constructor can be changed to the following: | ||
< | |||
%res is object resource | <p class="code">%res is object resource | ||
... | ... | ||
%res = newFromUrl('http://nfl.com/score') | %res = newFromUrl('http://nfl.com/score') | ||
</ | </p> | ||
Similarly, if a method called Send in class Coworker took a resource | Similarly, if a method called Send in class Coworker took a resource | ||
object as an input parameter, the NewFromUrl virtual constructor could | object as an input parameter, the NewFromUrl virtual constructor could | ||
also be used as follows: | also be used as follows: | ||
< | |||
%sufjan is object coworker | <p class="code">%sufjan is object coworker | ||
... | ... | ||
%sufjan:send(newFromUrl('http://asthmatickitty.com')) | %sufjan:send(newFromUrl('http://asthmatickitty.com')) | ||
</ | </p> | ||
Because the naming rules for virtual constructors are now | Because the naming rules for virtual constructors are now | ||
Line 383: | Line 386: | ||
change, without breaking existing applications. | change, without breaking existing applications. | ||
==Object variables within class definitions== | |||
Classes can, themselves, contain object variables: | Classes can, themselves, contain object variables: | ||
< | <p class="code">class pet | ||
class pet | |||
public | public | ||
... | ... | ||
Line 395: | Line 396: | ||
... | ... | ||
end class | end class | ||
</ | </p> | ||
Remember that an object variable is not really the object itself but | Remember that an object variable is not really the object itself but | ||
a reference to an underlying object so, in the case of an object | a reference to an underlying object so, in the case of an object | ||
Line 401: | Line 403: | ||
object, per se, but simply a reference to another object. | object, per se, but simply a reference to another object. | ||
It is possible for two different classes to each contain references | It is possible for two different classes to each contain references to each other. | ||
to each other. | |||
But for an object variable to be defined, the object class must already have | But for an object variable to be defined, the object class must already have | ||
been declared. | been declared. | ||
Line 408: | Line 409: | ||
be declared without the references before the other. | be declared without the references before the other. | ||
The simplest way to do this is with an empty Class block: | The simplest way to do this is with an empty Class block: | ||
< | |||
class human | <p class="code">class human | ||
end class | end class | ||
Line 429: | Line 430: | ||
... | ... | ||
end class | end class | ||
</ | </p> | ||
It is also possible for a class to contain object variables that | It is also possible for a class to contain object variables that | ||
refer to the containing class: | refer to the containing class: | ||
< | <p class="code">class pet | ||
class pet | |||
public | public | ||
... | ... | ||
Line 441: | Line 442: | ||
... | ... | ||
end class | end class | ||
</ | </p> | ||
In such a case, no empty Class block is necessary to declare the class | |||
because the Class block containing the object variable is sufficient | In such a case, no empty <var>Class</var> block is necessary to declare the class | ||
because the <var>Class</var> block containing the object variable is sufficient | |||
to declare the existence of the class. | to declare the existence of the class. | ||
The New | ==Discarding objects== | ||
The <var>New</var> constructor creates an instance of a class. | |||
This instance is maintained until it is either implicitly or explicitly | This instance is maintained until it is either implicitly or explicitly | ||
discarded (or destroyed in the terminology of some object-oriented languages). | discarded (or destroyed in the terminology of some object-oriented languages). | ||
===Discarding explicitly=== | |||
An underlying object can be discarded explicitly by invoking the Discard | An underlying object can be discarded explicitly by invoking the <var>Discard</var> | ||
method (system subroutine) against it: | method (system subroutine) against it: | ||
< | |||
%mick is object dog | <p class="code">%mick is object dog | ||
... | ... | ||
%mick = new('Terrier') | %mick = new('Terrier') | ||
Line 461: | Line 464: | ||
* Done with object pointed to by %mick | * Done with object pointed to by %mick | ||
%mick:discard | %mick:discard | ||
</ | </p> | ||
The Discard method never takes any input parameters and returns no value, | The Discard method never takes any input parameters and returns no value, | ||
so it is a Subroutine. | so it is a Subroutine. | ||
The Discard method is an AllowNullObject method because it can be invoked | The Discard method is an AllowNullObject method because it can be invoked | ||
with a null object, such as one that was never set: | with a null object, such as one that was never set: | ||
< | |||
%mick is object dog | <p class="code">%mick is object dog | ||
for %i from 1 to 10 | for %i from 1 to 10 | ||
Line 473: | Line 477: | ||
... | ... | ||
end for | end for | ||
</ | </p> | ||
In the first loop iteration, above, %mick would not be set, so the | |||
In the first loop iteration, above, <code>%mick</code> would not be set, so the | |||
<code>%mick:discard</code> call would not have anything to discard, | |||
but it would not indicate a request-cancelling null-pointer error. | but it would not indicate a request-cancelling null-pointer error. | ||
<blockquote class="note"> | |||
<p>'''Note:''' Not all objects may be discarded explicitly: </p> | |||
<ul> | |||
<li>Some objects are integral parts of other objects, and discarding one cannot be done without compromising the other. For example, you may not explicitly <var>Discard</var> an XmlNode object, though as discussed [[#xmlnode|below]], you may discard it by using the <var>DeepDiscard</var> method. | |||
<li>Some objects may be explicitly defined with a <var>[[Classes and Objects#Disallow clause|Disallow Discard]]</var> clause, which protects them from being discarded directly. | |||
</ul> | |||
</blockquote> | |||
===Discarding implicitly=== | |||
Objects may be discarded implicitly, when there are no | Objects may be discarded implicitly, when there are no | ||
more references to the underlying object. | more references to the underlying object. | ||
Line 485: | Line 498: | ||
the object. | the object. | ||
Since there is no reason to maintain the object any more, the | Since there is no reason to maintain the object any more, the | ||
<var class="product">Janus SOAP User Language Interface</var> automatically discards the object. | |||
The following code demonstrates this principle: | The following code demonstrates this principle: | ||
< | <p class="code">%bunny is object dog | ||
%bunny is object dog | |||
... | ... | ||
%bunny = new('Ibizan Hound') | %bunny = new('Ibizan Hound') | ||
Line 494: | Line 506: | ||
%bunny:feed | %bunny:feed | ||
%bunny = new('Yorkshire Terrier') | %bunny = new('Yorkshire Terrier') | ||
</ | </p> | ||
When the statement | When the statement <code>%bunny = new('Yorkshire terrier')</code> is executed, | ||
a new object is created, but it is no longer possible to access the object | a new object is created, but it is no longer possible to access the object | ||
instantiated by | instantiated by <code>%bunny = new('Ibizan hound')</code> — there are | ||
no longer any references to that object. | no longer any references to that object. | ||
Because of that, the first Dog object is automatically discarded when the | Because of that, the first Dog object is automatically discarded when the | ||
second Dog object is created and referenced by %bunny. | second Dog object is created and referenced by <code>%bunny</code>. | ||
If a second reference to that object had been set, it would not have been | If a second reference to that object had been set, it would not have been | ||
discarded: | discarded: | ||
< | |||
%bunny is object dog | <p class="code">%bunny is object dog | ||
%dallas is object dog | %dallas is object dog | ||
... | ... | ||
Line 513: | Line 525: | ||
%bunny:feed | %bunny:feed | ||
%bunny = new('Yorkshire terrier') | %bunny = new('Yorkshire terrier') | ||
</ | </p> | ||
A reference to an object can be inside another object: | A reference to an object can be inside another object: | ||
< | |||
class dog | <p class="code">class dog | ||
public | public | ||
variable name is string len 32 | variable name is string len 32 | ||
Line 530: | Line 542: | ||
%first = new('Josh', 'Newfoundland') | %first = new('Josh', 'Newfoundland') | ||
%first:nextCompetitor = new('Mick', 'Kerry Blue Terrier') | %first:nextCompetitor = new('Mick', 'Kerry Blue Terrier') | ||
</ | </p> | ||
In the above example, the object created by | In the above example, the object created by | ||
<code>new('Mick', 'Kerry Blue Terrier')</code> can be accessed | |||
via | via <code>%first:nextCompetitor</code>, so it will not be implicitly | ||
discarded. | discarded. | ||
In such a way, it is possible to build a chain of objects: | In such a way, it is possible to build a chain of objects: | ||
< | |||
%first is object dog | <p class="code">%first is object dog | ||
%last is object dog | %last is object dog | ||
%new is object dog | %new is object dog | ||
Line 554: | Line 566: | ||
%last:nextCompetitor = %new | %last:nextCompetitor = %new | ||
%last = %new | %last = %new | ||
</ | </p> | ||
In the above example, the object created by | In the above example, the object created by | ||
<code>new('Les', 'Pekingese')</code> can be accessed by | |||
<code>%first:nextCompetitor:nextCompetitor</code>, so it is not implicitly | |||
discarded. | discarded. | ||
Line 564: | Line 577: | ||
A '''cycle''' is a collection of objects where the objects are linked | A '''cycle''' is a collection of objects where the objects are linked | ||
in such a way as to form a ring: | in such a way as to form a ring: | ||
< | |||
%first is object dog | <p class="code">%first is object dog | ||
%last is object dog | %last is object dog | ||
%new is object dog | %new is object dog | ||
Line 577: | Line 590: | ||
%new = new('Les', 'Pekingese') | %new = new('Les', 'Pekingese') | ||
%last:nextCompetitor = %first | %last:nextCompetitor = %first | ||
</ | </p> | ||
A cycle can even be created with a single object: | A cycle can even be created with a single object: | ||
< | |||
%new is object dog | <p class="code">%new is object dog | ||
... | ... | ||
%new = new('Josh', 'Newfoundland') | %new = new('Josh', 'Newfoundland') | ||
%new:nextCompetitor = %new | %new:nextCompetitor = %new | ||
</ | </p> | ||
A web of objects can contain many cycles of objects. | A web of objects can contain many cycles of objects. | ||
Line 597: | Line 611: | ||
The following illustrates a case where two objects cannot be reached via | The following illustrates a case where two objects cannot be reached via | ||
any %variable, but they won't be discarded because they reference each other: | any %variable, but they won't be discarded because they reference each other: | ||
< | |||
%first is object dog | <p class="code">%first is object dog | ||
%last is object dog | %last is object dog | ||
%new is object dog | %new is object dog | ||
Line 608: | Line 622: | ||
%new = new('Les', 'Pekingese') | %new = new('Les', 'Pekingese') | ||
%first = %new | %first = %new | ||
</ | </p> | ||
Such objects will not be discarded by | |||
Most object-oriented languages solve this problem with [[Managing object storage|garbage collection]] — a process in which all objects are scanned to determine if they are "reachable" from some base variables (in | Such objects will not be discarded by <var class="product">SOUL</var> until the user logs off. | ||
Most object-oriented languages solve this problem with [[Global and session objects#Managing object storage|garbage collection]] — a process in which all objects are scanned to determine if they are "reachable" from some base variables (in <var class="product">SOUL</var> these would be local and common %variables). | |||
<var class="product">SOUL</var> has a <var>DeepDiscard</var> method, which provides an alternative approach to | |||
some of these cases that is less expensive than garbage collection and simpler than <var>Discard</var>. | |||
<var>DeepDiscard</var> is discussed in the next section. | |||
==Deep discard of objects== | |||
The Discard method ([[Global and session objects#Using system class methods to access global and session objects|Object class]] subroutine) is usually available to explicitly | |||
The Discard method (Object class subroutine) is usually available to explicitly | |||
discard the object referenced by an object variable. | discard the object referenced by an object variable. | ||
If an object contains a reference to another object (of the same or a | If an object contains a reference to another object (of the same or a | ||
Line 624: | Line 639: | ||
reference object, as shown in the following example. | reference object, as shown in the following example. | ||
The object referenced by | The object referenced by <code>%chain</code> contains | ||
a reference to another object. | a reference to another object. | ||
When | When <code>%chain</code> is discarded, the reference to the object with the name | ||
<code>Frack</code> is lost. | |||
Since this is the only reference to that object, the object is then implicitly | Since this is the only reference to that object, the object is then implicitly | ||
discarded. | discarded. | ||
< | |||
class linkedList | <p class="code">class linkedList | ||
public | public | ||
variable name is string len 32 | variable name is string len 32 | ||
Line 646: | Line 661: | ||
%chain:discard | %chain:discard | ||
</ | </p> | ||
Now suppose the class definition remains the same, but the code is changed to | Now suppose the class definition remains the same, but the code is changed to | ||
the following: | the following: | ||
< | |||
%chain is object linkedList | <p class="code">%chain is object linkedList | ||
%link is object linkedList | %link is object linkedList | ||
Line 660: | Line 676: | ||
%chain:discard | %chain:discard | ||
</ | </p> | ||
In this case, even when the object referenced by <code>%chain</code> is discarded, | |||
< | and its reference to the <code>Frack</code> object goes away, there is still a reference to | ||
% | the <code>Frack</code> object, namely, <code>%link</code>, so that object is not discarded. | ||
</ | |||
Since DeepDiscard cleans up an object cycle or forest, it is an | <var class="product">SOUL</var> also has a generic '''DeepDiscard''' method: | ||
<p class="code">%obj:DeepDiscard | |||
</p> | |||
Like the <var>Discard</var> method, <var>DeepDiscard</var> explicitly discards the method object. But, unlike Discard, it also explicitly discards all objects that are referenced directly or indirectly (a reference to an object that contains a reference to another object, and so on) by the object being discarded. This makes DeepDiscard useful for discarding an entire chain of objects, an object tree, an object cycle, or even an object forest, even if there are still references to some of the objects in the request. | |||
Since <var>DeepDiscard</var> cleans up an object cycle or forest, it is an | |||
efficient alternative to having garbage collection clean these up, if there is | efficient alternative to having garbage collection clean these up, if there is | ||
an obvious point in code where the object cycle or forest is no longer used. | an obvious point in code where the object cycle or forest is no longer used. | ||
Just as with the Discard method, there is a danger with DeepDiscard that | Just as with the <var>Discard</var> method, there is a danger with <var>DeepDiscard</var> that | ||
some reference to an explicitly discarded object might still be required. | some reference to an explicitly discarded object might still be required. | ||
But because it has a potential to | But because it has a potential to | ||
discard more objects, the risk is somewhat greater with DeepDiscard, | discard more objects, the risk is somewhat greater with <var>DeepDiscard</var>, | ||
so it should be used with caution. | so it should be used with caution. | ||
The DeepDiscard method cannot be used on any object in a class that | The <var>DeepDiscard</var> method cannot be used on any object in a class that | ||
<ul> | <ul> | ||
<li>is declared with a [[Classes and | <li>is declared with a <var>[[Classes and Objects#Disallow clause|Disallow Discard]]</var> clause | ||
<li>contains a reference to a class defined with Disallow Discard | <li>contains a reference to a class defined with <var>Disallow Discard</var> | ||
<li>contains an indirect reference to a class that is defined with Disallow Discard | <li>contains an indirect reference to a class that is defined with <var>Disallow Discard</var> | ||
</ul> | </ul> | ||
Without these rules, an explicit DeepDiscard an object could result in the explicit discard of an object of a class that expressly disallows explicit discards. | Without these rules, an explicit <var>DeepDiscard</var> of an object could result in the explicit discard of an object of a class that expressly disallows explicit discards. | ||
For most system classes, DeepDiscard works identically to Discard. | <div id="xmlnode"></div> | ||
For most system classes, <var>DeepDiscard</var> works identically to <var>Discard</var>. | |||
The exceptions to this are: | The exceptions to this are: | ||
<ul> | <ul> | ||
<li>XmlNode objects, which you may DeepDiscard but not Discard. | <li><var>Screenfield</var> objects, which you may <var>DeepDiscard</var> but not <var>Discard</var>. | ||
If you DeepDiscard an XmlNode, the underlying XmlDoc object is also discarded. | If you <var>DeepDiscard</var> a <var>Screenfield</var>, the underlying <var>Screen</var> object is also discarded. | ||
<li>XmlNodelist objects, which you may Discard or DeepDiscard. | |||
If you Discard an XmlNodelist, the XmlNodelist object (only) is discarded. | <li><var>XmlNode</var> objects, which you may <var>DeepDiscard</var> but not <var>Discard</var>. | ||
If you DeepDiscard an XmlNodelist, both the XmlNodelist object and the | If you <var>DeepDiscard</var> an <var>XmlNode</var>, the underlying <var>XmlDoc</var> object is also discarded. | ||
underlying XmlDoc object are discarded. | |||
<li><var>XmlNodelist</var> objects, which you may <var>Discard</var> or <var>DeepDiscard</var>. | |||
If you <var>Discard</var> an <var>XmlNodelist</var>, the <var>XmlNodelist</var> object (only) is discarded. | |||
If you <var>DeepDiscard</var> an <var>XmlNodelist</var>, both the <var>XmlNodelist</var> object and the | |||
underlying <var>XmlDoc</var> object are discarded. | |||
</ul> | </ul> | ||
==Working with null valued object variables== | |||
If an object variable does not refer to an object, either because | If an object variable does not refer to an object, either because | ||
it was never set, or because the object to which it | it was never set, or because the object to which it referred was | ||
discarded, it is considered to have a '''Null''' value. | discarded, it is considered to have a '''Null''' value. | ||
While, an object variable with a | While, an object variable with a <var>Null</var> value cannot be used as | ||
the object for most methods (except those declared with AllowNullObject), | the object for most methods (except those declared with <var>AllowNullObject</var>), | ||
they can be used in comparisons. | they can be used in comparisons. | ||
The most common comparison done with objects, in fact, is to test | The most common comparison done with objects, in fact, is to test | ||
whether the object is | whether the object is <var>Null</var>: | ||
< | |||
%first is object dog | <p class="code">%first is object dog | ||
%new is object dog | %new is object dog | ||
... | ... | ||
Line 722: | Line 746: | ||
end of | end of | ||
%last = new | %last = new | ||
</ | </p> | ||
If a null object variable is compared with another object variable, | If a null object variable is compared with another object variable, | ||
it is considered equal if, and only if, the other object variable is | it is considered equal if, and only if, the other object variable is | ||
Line 728: | Line 753: | ||
A null object variable can be assigned to another object variable: | A null object variable can be assigned to another object variable: | ||
< | <p class="code">%first is object dog | ||
%first is object dog | |||
%new is object dog | %new is object dog | ||
%new = %first | %new = %first | ||
</ | </p> | ||
And a variable can be explicitly set to Null: | And a variable can be explicitly set to Null: | ||
< | <p class="code">%winner is object dog | ||
%winner is object dog | |||
... | ... | ||
%winner = null | %winner = null | ||
</ | </p> | ||
In both comparisons and assignments, | In both comparisons and assignments, <var>Null</var> is actually a shorthand for a [[Shared class members|shared]] null property on the class, and it can be indicated as such: | ||
< | <p class="code">%winner is object dog | ||
%winner is object dog | |||
... | ... | ||
%winner = %(dog):null | %winner = %(dog):null | ||
Line 749: | Line 772: | ||
if %winner eq %(dog):null | if %winner eq %(dog):null | ||
... | ... | ||
</ | </p> | ||
The value | The value <var>Null</var> can also be passed as an input parameter to | ||
a method: | a method: | ||
< | <p class="code">class show | ||
class show | |||
public | public | ||
... | ... | ||
Line 768: | Line 790: | ||
%minScore = %akc:score(null) | %minScore = %akc:score(null) | ||
... | ... | ||
</ | </p> | ||
: | <p class="note">'''Note:''' Assigning a value to an object property, that is to a Property that | ||
Assigning a value to an object property, that is to a Property that | returns an object, is a special case of passing an input parameter to a method. | ||
returns an object, | |||
is a special case of passing an input parameter to a method. | |||
The value assigned is | The value assigned is | ||
an implicit parameter of the Set method of the property. | an implicit parameter of the <var>Set</var> method of the property. | ||
In such cases, you may validly assign a Null value, | In such cases, you may validly assign a Null value, | ||
as if the implicit parameter included an implicit AllowNull keyword. | as if the implicit parameter included an implicit AllowNull keyword. | ||
</p> | |||
<p> | |||
An object that is explicitly discarded causes all references to that | An object that is explicitly discarded causes all references to that | ||
object to become null: | object to become null: </p> | ||
< | <p class="code">%first is object dog | ||
%first is object dog | |||
%new is object dog | %new is object dog | ||
Line 789: | Line 808: | ||
%first = %new | %first = %new | ||
%first:discard | %first:discard | ||
</ | </p> | ||
In the above example, both | In the above example, both <code>%first</code> and <code>%new</code> are | ||
set to Null after the | set to <var>Null</var> after the <code>%first:discard</code>. | ||
==Passing object variables as parameters== | |||
Object variables can be passed as method or complex subroutine parameters: | Object variables can be passed as method or complex subroutine parameters: | ||
< | <p class="code">class clown | ||
class clown | |||
public | public | ||
subroutine honk(%nose is object nose) | subroutine honk(%nose is object nose) | ||
Line 808: | Line 826: | ||
... | ... | ||
%bozo:honk(%schnoz) | %bozo:honk(%schnoz) | ||
</ | </p> | ||
By default, passing a null object variable as a method or complex subroutine | By default, passing a null object variable as a method or complex subroutine | ||
Line 815: | Line 833: | ||
qualifier in the method or complex subroutine declaration to indicate that | qualifier in the method or complex subroutine declaration to indicate that | ||
the method or complex subroutine will accept null objects: | the method or complex subroutine will accept null objects: | ||
< | <p class="code">class clown | ||
class clown | |||
public | public | ||
subroutine honk(%nose is object nose allowNull) | subroutine honk(%nose is object nose allowNull) | ||
Line 823: | Line 840: | ||
... | ... | ||
end class | end class | ||
</ | </p> | ||
An Output object variable is always allowed to be null and can be set | An Output object variable is always allowed to be null and can be set | ||
by a method: | by a method: | ||
< | |||
class clown | <p class="code">class clown | ||
public | public | ||
subroutine honk(%nose is object nose output) | subroutine honk(%nose is object nose output) | ||
Line 844: | Line 861: | ||
%bozo = new | %bozo = new | ||
%bozo:honk(%schnoz) | %bozo:honk(%schnoz) | ||
</ | </p> | ||
In this example %schnoz would be set to reference a new instance of | |||
a | In this example, <code>%schnoz</code> would be set to reference a new instance of | ||
a <code>nose</code> object that was instantiated by the <code>honk</code> method. | |||
An Input object variable is passed by value to a method | An Input object variable is passed by value to a method | ||
Line 857: | Line 875: | ||
are allowed, but they are ''not'' propagated to the outer object. | are allowed, but they are ''not'' propagated to the outer object. | ||
In the example below, the subroutine modifies the %schnoz object color | In the example below, the subroutine modifies the <code>%schnoz</code> object color, | ||
but the reassignment to %schnozola does not affect %schnoz: | but the reassignment to <code>%schnozola</code> does not affect <code>%schnoz</code>: | ||
< | <p class="code">class clown | ||
class clown | |||
public | public | ||
subroutine honk(%nose is object nose output) | subroutine honk(%nose is object nose output) | ||
Line 887: | Line 904: | ||
%bozo = new | %bozo = new | ||
%bozo:honk(%schnoz) | %bozo:honk(%schnoz) | ||
</ | </p> | ||
: | |||
A case where a method significantly affects the object referenced by | <blockquote class="note">'''Note:''' A case where a method significantly affects the object referenced by an Input object variable is the Discard method. | ||
an Input object variable is the Discard method. | |||
A Discard method invoked against an Input parameter sets | A Discard method invoked against an Input parameter sets | ||
the Input parameter and call argument to Null. | the Input parameter and call argument to Null. | ||
This can happen directly: | This can happen directly: | ||
< | |||
subroutine honk (%nose is object nose input) | <p class="code">subroutine honk (%nose is object nose input) | ||
%nose:discard | %nose:discard | ||
end subroutine | end subroutine | ||
</ | </p> | ||
Or, it can happen indirectly: | Or, it can happen indirectly: | ||
< | |||
subroutine honk (%nose is object nose input) | <p class="code">subroutine honk (%nose is object nose input) | ||
%beak is object nose | %beak is object nose | ||
Line 914: | Line 929: | ||
end subroutine | end subroutine | ||
</ | </p> | ||
</blockquote> | |||
==See also== | |||
<ul> | |||
<li>[[Classes and Objects]] | |||
<li>[[Copying objects]] | |||
<li>[[Global and session objects]] | |||
<li>[[Managing server space for objects]] | |||
<li>[[Object oriented programming in SOUL]] | |||
</ul> | |||
[[Category:Overviews]] | |||
[[Category:SOUL object-oriented programming topics]] |
Latest revision as of 19:05, 15 August 2018
An object variable is a proxy or substitute for an object. You do not access or perform actions against an object directly but instead via operations on an object variable that references the object. This page discusses how you create, work with, and discard object variables.
Why use an object reference instead of an object
Object variables are used to declare variables that refer to instances of a class. Despite their name, they are not really objects, as such, but references or pointers to objects. The underlying objects themselves are never directly accessible and can only be accessed via an object reference. This concept of reference variables being used to access underlying objects is common to all object-oriented languages. There are two, primary reasons for this:
- Objects can get quite large, and moving them around an application rather than moving references around would be extremely expensive.
- Objects usually represent some "real world" entity. To have multiple copies of such an entity in an application creates the possibility that these copies would get out of synch, causing application complexity and even bugs.
Assignment and comparison operations are probably the two most significant places to note the distinction between object variables being references to the objects or the objects themselves. The following is an example of assignment of an object reference, rather than an object:
class pet public variable type is string len 16 end public end class ... %rover is object pet %fluffy is object pet ... %rover:type = 'Dog' %fluffy = %rover %fluffy:type = 'Cat' print %rover:type
Because the statement %fluffy = %rover
assigns to %fluffy a reference
to the same object as %rover, both variables end up referring
to the same object.
The statement %fluffy:type = 'Cat'
therefore changes
the underlying object pointed to by %rover (since it's the same object
as the one pointed to by %fluffy).
As a result, the Print statement prints Cat
.
As assignment assigns references, comparison compares references. That is, a comparison between two object references does not compare the objects being referred to, but it instead compares whether two object references point to the same object.
Consider the following example::
class pet public variable type is string len 16 end public end class ... %rover is object pet %fluffy is object pet ... %rover = new %fluffy = new %rover:type = 'Dog' %fluffy:type = 'Dog' if %fluffy eq %rover then print 'They''re equal!' end if
This will not print They're equal
.
Even though the objects
pointed to by %fluffy and %rover have the same variable values, they
are different objects, so %fluffy is not equal to %rover.
However, if the code were changed:
class pet public variable type is string len 16 end public end class ... %rover is object pet %fluffy is object pet ... %rover:type = 'Dog' %rover = %fluffy if %fluffy eq %rover then print 'They''re equal!' end if
They're equal
will be printed, because %rover = %fluffy
sets %rover
to point to the same object as %fluffy
, making them equal.
Note: Since order comparisons (GE, LE, GT, and LT) are meaningless for references — when is a reference to one object greater than a reference to another? — only equality and inequality (EQ and NE) comparisons are allowed for objects.
Despite the foregoing remarks, for most purposes, one can still think of object variables as being the objects themselves, rather than references to the object. For example, in the statement:
%rover:type = 'Ferret'
it is quite natural to think of the statement as setting %rover's Type to "ferret," rather than as setting the type of the object referenced by %rover to "Ferret."
Creating object instances
Since object variables are references to objects, two questions naturally arise: what object does an object variable refer to initially, and where does the referenced object come from? The answer to the first question is that an object variable initially points to no object. So, the following program would cause a request cancellation error:
class pet public variable type is string len 16 end public end class ... %rover is object pet %rover:type = 'Dog'
The statement %rover:type = 'Dog'
attempts to set the object's
type to "Dog," but there is no instance of an object for
which to set the type.
This kind of error is sometimes referred to as a null pointer
exception.
So how, then, is an object created? The answer is via a Constructor, usually the default one named New.
Using New or other Constructors
When you invoke a Constructor method, SOUL creates an object instance which is available to the Constructor code. The Constructor:
- Operates on the new instance (which is the value of
%this
within the Constructor)Unlike a shared method, a Constructor operates on an object instance.
- Returns the value of
%this
as the result.Like a Subroutine, the Constructor's Return statement may not specify a value, but like a Function, the Constructor does produce a result.
In any non-Abstract class, you can invoke a default Constructor, named New, which operates no further on %this
and simply returns a new instance for explicit or implicit assignment. As described below, you can explicitly define New to operate on the new instance, you can define other constructors in addition to New, and you can also "disallow" New from use outside the class definition.
Syntax for Constructors
The syntax for invoking a Constructor is the same as the syntax for invoking a Shared method which returns an instance of the class.
To invoke the New constructor and assign its result:
%rover = new
You can explicitly indicate the class of the object as follows:
%rover = %(pet):new
While specifying the class in this way is unnecessary and might be viewed as detrimental because it results in the class name being sprinkled around inside application code, it might also be viewed as beneficial — making the object class clear in the part of code where the object is created.
Customizing a Constructor
Sometimes it is useful to run code when creating an instance of a class. In such cases, you can explicitly define the New Constructor:
class pet public variable initTime is float constructor new end public constructor new %initTime = $sir_datens end constructor end class
In the above example, the class variable initTime
is set
to the time the instance of the object is created.
That code runs when the New Constructor is invoked in a program to create a pet
instance:
%josh is object pet ... %josh = new
Sometimes it is useful to have a constructor take parameters. For example, if it doesn't make sense to have a Pet object without a Type, make the New constructor require an input parameter that sets the class Type variable:
class pet public variable type is string len 16 constructor new(%type is string len 16) end public constructor new(%type is string len 16) %this:type = %type end constructor end class
The parameters must be specified on the New constructor:
%snoopy is object pet ... %snoopy = new('Beagle')
As of Sirius Mods Version 7.2,
constructors are allowed to change the object being constructed.
For example, variable ExistingObj
assigns
an existing Dachsie
object when constructor Newbie
is called:
class dachsie public ... constructor newbie ... end public public shared variable ExistingObj is object dachsie end public shared constructor newbie %this = %(this):existingObj end constructor ... end class
However, this feature was actually provided for a different purpose,
and using it in the context shown above requires caution.
While %this
is an input parameter to the Constructor, changing it changes the
object returned to the Constructor invoker, so it acts almost like an output parameter.
Modifying %this
within the Constructor can also produce unexpected behavior
if the Constructor is itself called from an extension class Construct statement.
Instead of using a Constructor to return a modified object as in the example above, you should consider using a "factory method".
Other ways to use a Constructor
A Constructor can also be used in these ways:
- To specify a new instance of a class as an input parameter to a method:
class pet public ... subroutine compare(%pet is object pet) ... end public ... end class ... %lassie is object pet ... %lassie:compare( new('Collie') )
- Without being explicitly specified, to automatically create an instance of an object when an object variable is first referenced. For such instantiation, you must specify Auto New in the object's declaration.
Using multiple Constructors
Most object-oriented languages support only a single constructor name for a class. If multiple constructors are needed, this is achieved with overloading — the use of different methods with the same name that are distinguished by their parameter lists. Overloading, however, can be confusing, and it is limited: you can't have multiple methods with the same name and the same datatypes in the parameter list.
SOUL supports multiple constructors:
class resource public constructor newFromUrl(%url is string len 255) constructor newFromProc(%proc is string len 255) ... end public ... end class ... %res is object resource ... %res = newFromUrl('http://nfl.com/score') ... %res = newFromProc('LOCAL.RESOURCE.PROC') ... %res = new
This example illustrates:
- Constructors not called New are invoked in exactly the same way
as the New constructor.
A function that invokes a constructor can be used in any other context in which the New Constructor can be used.
- It is recommended that you always begin Constructor names with the word "New" to distinguish them from other methods.
Private Constructors
Although this section has discussed only public constructors, that is, constructors defined for use outside the class definition, you may also define private constructors, which may only be invoked from inside the class. You declare such a Constructor in the Private block of a class definition, and you define it with your other methods after the declaration blocks.
If you want to define a private constructor named New, however,
you must make sure to specify Disallow New
in the Public block of the class definition (see "Declaration block
syntax").
No other private constructors require an additional Disallow New in the
Public block.
Virtual constructors
It is possible to have a non-constructor shared function return an instance of a class:
class resource public shared function newFromUrl(%url is string len 255) - is object resource ... end public shared function newFromUrl(%url is string len 255) - is object resource %return is object resource print 'In the factory' %return = new end function ... end class ... %res is object resource ... %res = %res:newFromUrl('http://nfl.com/score')
Such functions are often called factory methods or virtual constructors. As the example illustrates, two things distinguish factory methods from constructors:
- They can run code before the object is instantiated.
- They might not actually instantiate an object but return a reference to an existing instance. Such a method may not truly be a factory method.
The support for multiple constructors means that SOUL users need factory methods less than those using most other object-oriented programming languages.
Virtual constructors can be invoked in much the same way that regular constructors are invoked, that is, without specifying the class name if the class name can be determined from context. For example, the above example of the NewFromUrl virtual constructor can be changed to the following:
%res is object resource ... %res = newFromUrl('http://nfl.com/score')
Similarly, if a method called Send in class Coworker took a resource object as an input parameter, the NewFromUrl virtual constructor could also be used as follows:
%sufjan is object coworker ... %sufjan:send(newFromUrl('http://asthmatickitty.com'))
Because the naming rules for virtual constructors are now identical to that for regular constructors, the two can be used interchangeably, and one can be changed to the other as requirements change, without breaking existing applications.
Object variables within class definitions
Classes can, themselves, contain object variables:
class pet public ... variable owner is object human ... end public ... end class
Remember that an object variable is not really the object itself but a reference to an underlying object so, in the case of an object variable inside a class block, the class does not contain another object, per se, but simply a reference to another object.
It is possible for two different classes to each contain references to each other. But for an object variable to be defined, the object class must already have been declared. So if two classes contain references to each other, at least one must be declared without the references before the other. The simplest way to do this is with an empty Class block:
class human end class class pet public ... variable owner is object human ... end public ... end class ... class human public ... variable dog is object pet ... end public ... end class
It is also possible for a class to contain object variables that refer to the containing class:
class pet public ... variable sibling is object pet ... end public ... end class
In such a case, no empty Class block is necessary to declare the class because the Class block containing the object variable is sufficient to declare the existence of the class.
Discarding objects
The New constructor creates an instance of a class. This instance is maintained until it is either implicitly or explicitly discarded (or destroyed in the terminology of some object-oriented languages).
Discarding explicitly
An underlying object can be discarded explicitly by invoking the Discard method (system subroutine) against it:
%mick is object dog ... %mick = new('Terrier') ...
- Done with object pointed to by %mick
%mick:discard
The Discard method never takes any input parameters and returns no value, so it is a Subroutine. The Discard method is an AllowNullObject method because it can be invoked with a null object, such as one that was never set:
%mick is object dog for %i from 1 to 10 %mick:discard ... end for
In the first loop iteration, above, %mick
would not be set, so the
%mick:discard
call would not have anything to discard,
but it would not indicate a request-cancelling null-pointer error.
Note: Not all objects may be discarded explicitly:
- Some objects are integral parts of other objects, and discarding one cannot be done without compromising the other. For example, you may not explicitly Discard an XmlNode object, though as discussed below, you may discard it by using the DeepDiscard method.
- Some objects may be explicitly defined with a Disallow Discard clause, which protects them from being discarded directly.
Discarding implicitly
Objects may be discarded implicitly, when there are no more references to the underlying object. If there are no references to an underlying object, there is no way to retrieve or set values in the object or to invoke methods against the object. Since there is no reason to maintain the object any more, the Janus SOAP User Language Interface automatically discards the object. The following code demonstrates this principle:
%bunny is object dog ... %bunny = new('Ibizan Hound') %bunny:show %bunny:feed %bunny = new('Yorkshire Terrier')
When the statement %bunny = new('Yorkshire terrier')
is executed,
a new object is created, but it is no longer possible to access the object
instantiated by %bunny = new('Ibizan hound')
— there are
no longer any references to that object.
Because of that, the first Dog object is automatically discarded when the
second Dog object is created and referenced by %bunny
.
If a second reference to that object had been set, it would not have been
discarded:
%bunny is object dog %dallas is object dog ... %bunny = new('German Shepherd') %dallas = %bunny %bunny:show %bunny:feed %bunny = new('Yorkshire terrier')
A reference to an object can be inside another object:
class dog public variable name is string len 32 variable breed is string len 32 variable nextCompetitor is object ... end public end class ... %first is object dog ... %first = new('Josh', 'Newfoundland') %first:nextCompetitor = new('Mick', 'Kerry Blue Terrier')
In the above example, the object created by
new('Mick', 'Kerry Blue Terrier')
can be accessed
via %first:nextCompetitor
, so it will not be implicitly
discarded.
In such a way, it is possible to build a chain of objects:
%first is object dog %last is object dog %new is object dog ... %new = new('Josh', 'Newfoundland') %first = %new %last = %new %new = new('Mick', 'Kerry Blue Terrier') %last:nextCompetitor = %new %last = %new %new = new('Les', 'Pekingese') %last:nextCompetitor = %new %last = %new %new = new('Bunny', 'Ibizan Hound') %last:nextCompetitor = %new %last = %new
In the above example, the object created by
new('Les', 'Pekingese')
can be accessed by
%first:nextCompetitor:nextCompetitor
, so it is not implicitly
discarded.
By linking objects in such a way, it is possible to create a wide variety of object super-structures, including chains, trees, webs, and cycles. A cycle is a collection of objects where the objects are linked in such a way as to form a ring:
%first is object dog %last is object dog %new is object dog ... %new = new('Josh', 'Newfoundland') %first = %new %last = %new %new = new('Mick', 'Kerry Blue Terrier') %last:nextCompetitor = %new %last = %new %new = new('Les', 'Pekingese') %last:nextCompetitor = %first
A cycle can even be created with a single object:
%new is object dog ... %new = new('Josh', 'Newfoundland') %new:nextCompetitor = %new
A web of objects can contain many cycles of objects.
If a cycle of objects is created, each object in the cycle will always have at least one reference to it — the previous item in the cycle. This means that even if all other references to the objects in a cycle were eliminated, the objects would still not be discarded because they would each still have a reference to them, even though those references, themselves, aren't reachable from any variables in the request. The following illustrates a case where two objects cannot be reached via any %variable, but they won't be discarded because they reference each other:
%first is object dog %last is object dog %new is object dog ... %first = new('Josh', 'Newfoundland') %new = new('Mick', 'Kerry Blue Terrier') %first:nextCompetitor = %new %new:nextCompetitor = %first %new = new('Les', 'Pekingese') %first = %new
Such objects will not be discarded by SOUL until the user logs off. Most object-oriented languages solve this problem with garbage collection — a process in which all objects are scanned to determine if they are "reachable" from some base variables (in SOUL these would be local and common %variables).
SOUL has a DeepDiscard method, which provides an alternative approach to some of these cases that is less expensive than garbage collection and simpler than Discard. DeepDiscard is discussed in the next section.
Deep discard of objects
The Discard method (Object class subroutine) is usually available to explicitly discard the object referenced by an object variable. If an object contains a reference to another object (of the same or a different class), an explicit Discard results in the elimination of the reference. This removal of the reference can result in an implicit discard of the reference object, as shown in the following example.
The object referenced by %chain
contains
a reference to another object.
When %chain
is discarded, the reference to the object with the name
Frack
is lost.
Since this is the only reference to that object, the object is then implicitly
discarded.
class linkedList public variable name is string len 32 variable next is object linkedList end public end class ... %chain is object linkedList %chain = new %chain:name = 'Frick' %chain:next = new %chain:next:name = 'Frack' %chain:discard
Now suppose the class definition remains the same, but the code is changed to the following:
%chain is object linkedList %link is object linkedList %chain = new %chain:name = 'Frick' %chain:next = new %chain:next:name = 'Frack' %link = %chain:next %chain:discard
In this case, even when the object referenced by %chain
is discarded,
and its reference to the Frack
object goes away, there is still a reference to
the Frack
object, namely, %link
, so that object is not discarded.
SOUL also has a generic DeepDiscard method:
%obj:DeepDiscard
Like the Discard method, DeepDiscard explicitly discards the method object. But, unlike Discard, it also explicitly discards all objects that are referenced directly or indirectly (a reference to an object that contains a reference to another object, and so on) by the object being discarded. This makes DeepDiscard useful for discarding an entire chain of objects, an object tree, an object cycle, or even an object forest, even if there are still references to some of the objects in the request.
Since DeepDiscard cleans up an object cycle or forest, it is an efficient alternative to having garbage collection clean these up, if there is an obvious point in code where the object cycle or forest is no longer used.
Just as with the Discard method, there is a danger with DeepDiscard that some reference to an explicitly discarded object might still be required. But because it has a potential to discard more objects, the risk is somewhat greater with DeepDiscard, so it should be used with caution.
The DeepDiscard method cannot be used on any object in a class that
- is declared with a Disallow Discard clause
- contains a reference to a class defined with Disallow Discard
- contains an indirect reference to a class that is defined with Disallow Discard
Without these rules, an explicit DeepDiscard of an object could result in the explicit discard of an object of a class that expressly disallows explicit discards.
For most system classes, DeepDiscard works identically to Discard. The exceptions to this are:
- Screenfield objects, which you may DeepDiscard but not Discard. If you DeepDiscard a Screenfield, the underlying Screen object is also discarded.
- XmlNode objects, which you may DeepDiscard but not Discard. If you DeepDiscard an XmlNode, the underlying XmlDoc object is also discarded.
- XmlNodelist objects, which you may Discard or DeepDiscard. If you Discard an XmlNodelist, the XmlNodelist object (only) is discarded. If you DeepDiscard an XmlNodelist, both the XmlNodelist object and the underlying XmlDoc object are discarded.
Working with null valued object variables
If an object variable does not refer to an object, either because it was never set, or because the object to which it referred was discarded, it is considered to have a Null value. While, an object variable with a Null value cannot be used as the object for most methods (except those declared with AllowNullObject), they can be used in comparisons.
The most common comparison done with objects, in fact, is to test whether the object is Null:
%first is object dog %new is object dog ... %new = new('Josh', 'Newfoundland') if %first eq null then %first = new else %last:nextCompetitor = %new end of %last = new
If a null object variable is compared with another object variable, it is considered equal if, and only if, the other object variable is null.
A null object variable can be assigned to another object variable:
%first is object dog %new is object dog %new = %first
And a variable can be explicitly set to Null:
%winner is object dog ... %winner = null
In both comparisons and assignments, Null is actually a shorthand for a shared null property on the class, and it can be indicated as such:
%winner is object dog ... %winner = %(dog):null ... if %winner eq %(dog):null ...
The value Null can also be passed as an input parameter to a method:
class show public ... function score(%entrant is object dog allowNull) - is float ... end public ... end class ... %winner is object dog %akc is object show ... %minScore = %akc:score(null) ...
Note: Assigning a value to an object property, that is to a Property that returns an object, is a special case of passing an input parameter to a method. The value assigned is an implicit parameter of the Set method of the property. In such cases, you may validly assign a Null value, as if the implicit parameter included an implicit AllowNull keyword.
An object that is explicitly discarded causes all references to that object to become null:
%first is object dog %new is object dog %new = new('Dallas', 'German Shepherd') %first = %new %first:discard
In the above example, both %first
and %new
are
set to Null after the %first:discard
.
Passing object variables as parameters
Object variables can be passed as method or complex subroutine parameters:
class clown public subroutine honk(%nose is object nose) ... end public ... end class ... %bozo is object clown %schnoz is object nose ... %bozo:honk(%schnoz)
By default, passing a null object variable as a method or complex subroutine parameter causes request cancellation. However, the AllowNull keyword can be specified as an Input parameter qualifier in the method or complex subroutine declaration to indicate that the method or complex subroutine will accept null objects:
class clown public subroutine honk(%nose is object nose allowNull) ... end public ... end class
An Output object variable is always allowed to be null and can be set by a method:
class clown public subroutine honk(%nose is object nose output) ... end public subroutine honk(%nose is object nose output) %nose = new('red') end subroutine ... end class ... %bozo is object clown %schnoz is object nose ... %bozo = new %bozo:honk(%schnoz)
In this example, %schnoz
would be set to reference a new instance of
a nose
object that was instantiated by the honk
method.
An Input object variable is passed by value to a method and may be modified within the method. The method actually receives a copy of a pointer to the object. The copied pointer may be used to modify the referenced object, and the modifications are propagated to the object outside the subroutine. Changes to the pointer like assignment or new instantiation, are allowed, but they are not propagated to the outer object.
In the example below, the subroutine modifies the %schnoz
object color,
but the reassignment to %schnozola
does not affect %schnoz
:
class clown public subroutine honk(%nose is object nose output) ... property color is string ... end public subroutine honk(%nose is object nose output) %schnozola is object nose If %clown eq %bozo 'red' = %nose:color ... %nose = %schnozola ... end subroutine ... property color is string ... end property color end class ... %bozo is object clown %schnoz is object nose ... %bozo = new %bozo:honk(%schnoz)
Note: A case where a method significantly affects the object referenced by an Input object variable is the Discard method.
A Discard method invoked against an Input parameter sets the Input parameter and call argument to Null.
This can happen directly:
subroutine honk (%nose is object nose input) %nose:discard end subroutine
Or, it can happen indirectly:
subroutine honk (%nose is object nose input) %beak is object nose %beak = %nose %beak:discard end subroutine