Object variables: Difference between revisions

From m204wiki
Jump to navigation Jump to search
 
(40 intermediate revisions by 6 users not shown)
Line 1: Line 1:
==Object variables==
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, <i>per se</i>,
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:
<pre>
 
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
</pre>
</p>
Because the statement '''%fluffy = %rover''' assigns to %fluffy a reference
 
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 '''%fluffy:type = 'Cat'''' therefore changes
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 "Cat".
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::
<pre>
<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''re equal!'
   print 'They&apos;&apos;re equal!'
end if
end if
</pre>
</p>
This will not print "They're equal".
 
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:
<pre>
 
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''re equal!'
   print 'They&apos;&apos;re equal!'
end if
end if
</pre>
</p>
"They're equal" will be printed, because '''%rover = %fluffy'''
 
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>
:note.
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 &mdash;
Since order comparisons (GE, LE, GT, and LT) are meaningless for references &mdash;
when is a reference to one object greater than a reference to another? &mdash;
when is a reference to one object greater than a reference to another? &mdash;
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>
:enote.


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:
<pre>
 
%rover:type = 'Ferret'
<p class="code">%rover:type = 'Ferret'
</pre>
</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===


==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
where do the objects referred to come from?
where does the referenced object come from?
The answer to the first question is that object variables initially
The answer to the first question is that an object variable initially
point to ''no'' objects.
points to ''no'' object.
So, the following program would cause a request cancellation error:
So, the following program would cause a request cancellation error:
<pre>
 
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'
</pre>
</p>
The statement '''%rover:type = 'Dog'''' attempts to set the object's
 
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 135: Line 141:


So how, then, is an object created?
So how, then, is an object created?
The answer is via a special method called '''New'''.
The answer is via a <var>Constructor</var>, usually the default one named <var>New</var>.
====Using New or other constructors====
 
By default, each non-<var>Abstract</var> class contains a default <var>Constructor</var>, named
===Using New or other Constructors===
<var>New</var> (although it is <var>Private</var> if <var>Disallow</var> <var>New</var> is specified in the <var>Public</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>:
block). A <var>Constructor</var> is not the same as a <var>Function</var> or <var>Subroutine</var>:
<ul>
<ul>
<li>It operates on (i.e., the value of <code>%this</code> within the <var>Constructor</var>) a
<li>Operates on the new instance (which is the value of <code>%this</code> within the <var>Constructor</var>)
newly created instance of the class.
<p>Unlike a shared method, a <var>Constructor</var> operates on an object instance.</p>
<li>The value of <code>%this</code> is returned as the result;
 
like a <var>Subroutine</var>, the <var>Return</var> statement may not specify a value, but
<li>Returns the value of <code>%this</code> as the result.
like a <var>Function</var>, it does produce a result.
<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>
You can invoke the <var>New</var> constructor and assign its result as follows:
 
<pre>
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.
%rover = new
 
</pre>
====Syntax for Constructors====
The class of the object can be indicated with New as follows:
<pre>
%rover = %(pet):new
</pre>
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, a class can explicitly define the <var>New</var> <var>Constructor</var>:
In such cases, you can explicitly define the <var>New</var> <var>Constructor</var>:
<pre>
<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
</pre>
</p>


In the above example, the class variable '''initTime''' is set
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.
The New constructor is invoked in a program to create a <code>pet</code> instance:
That code runs when the <var>New</var> <var>Constructor</var> is invoked in a program to create a <code>pet</code> instance:
<pre>
<p class="code">%josh  is object pet
%josh  is object pet
  ...
  ...
%josh = new
%josh = new
</pre>
</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:
<pre>
<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
</pre>
</p>


The parameters
The parameters
must be specified on the New constructor:
must be specified on the <var>New</var> constructor:
<pre>
<p class="code">%snoopy  is object pet
%snoopy  is object pet
  ...
  ...
%snoopy = new('Beagle')
%snoopy = new('Beagle')
</pre>
</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:
<pre>
<p class="code">class dachsie
class dachsie
   public
   public
     ...
     ...
Line 231: Line 237:
   ...
   ...
end class
end class
</pre>
</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 '''%this''' is an input parameter to the constructor, changing it changes the
While <code>%this</code> is an input parameter to the <var>Constructor</var>, changing it changes the
object returned to the constructor invoker, so it acts almost like an output parameter.
object returned to the <var>Constructor</var> invoker, so it acts almost like an output parameter.
Modifying %this within the constructor can also produce unexpected behavior
Modifying <code>%this</code> within the <var>Constructor</var> can also produce unexpected behavior
if the constructor is itself called from an [[Inheritance and polymorphism|extension class Construct statement]].
if the <var>Constructor</var> is itself called from an [[Inheritance and polymorphism|extension class Construct statement]].


Instead of using a constructor to return a modified object as in the example
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"]].


The <var>New</var> <var>Constructor</var> can also be used in these ways:
====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
<pre>
class pet
   public
   public
     ...
     ...
Line 260: Line 265:
  ...
  ...
%lassie:compare( new('Collie') )
%lassie:compare( new('Collie') )
</pre>
</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 objects#The Auto keyword on object declarations|Auto New]] in the object's declaration.
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 &mdash;
If multiple constructors are needed, this is achieved with overloading &mdash;
Line 276: Line 282:
parameter list.
parameter list.


The [[Janus SOAP User Language Interface]] supports '''multiple constructors''':
<var class="product">SOUL</var> supports '''multiple constructors''':
<pre>
<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
</pre>
</p>


:listh
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 &mdash; by assignment from the name of a function
as the <var>New</var> constructor.
with the same name as the constructor.
<p>
This function creates an instance of the class,
calls the constructor with the same name, then
returns a reference to the created instance.
 
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 constructor names with the
<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 constructor in the Private block of a class definition,
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 '''Disallow New'''
you must make sure to specify <code>Disallow New</code>
in the Public block of the class definition (see :figref refid=bloksyn.).
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.
====Virtual Constructor methods====


===<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:
<pre>
 
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')
</pre>
</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 [[Janus SOAP User Language Interface]]
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:
<pre>
 
%res  is object resource
<p class="code">%res  is object resource
   ...
   ...
%res = newFromUrl('http://nfl.com/score')
%res = newFromUrl('http://nfl.com/score')
</pre>
</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:
<pre>
 
%sufjan  is object coworker
<p class="code">%sufjan  is object coworker
  ...
  ...
%sufjan:send(newFromUrl('http://asthmatickitty.com'))
%sufjan:send(newFromUrl('http://asthmatickitty.com'))
</pre>
</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===
==Object variables within class definitions==
 
Classes can, themselves, contain object variables:
Classes can, themselves, contain object variables:
<pre>
<p class="code">class pet
class pet
   public
   public
     ...
     ...
Line 395: Line 396:
  ...
  ...
end class
end class
</pre>
</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:
<pre>
 
class human
<p class="code">class human
end class
end class
   
   
Line 429: Line 430:
  ...
  ...
end class
end class
</pre>
</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:
<pre>
<p class="code">class pet
class pet
   public
   public
     ...
     ...
Line 441: Line 442:
  ...
  ...
end class
end class
</pre>
</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.
===Discarding objects===


The New function creates an instance of a class.
==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====
===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:
<pre>
 
%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
</pre>
</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:
<pre>
 
%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
</pre>
</p>
In the first loop iteration, above, %mick would not be set, so the
 
'''%mick:discard''' call would not have anything to discard,
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.
====Discarding implicitly====


<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
[[Janus SOAP User Language Interface]] automatically discards the object.
<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:
<pre>
<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')
</pre>
</p>


When the statement '''%bunny = new('Yorkshire terrier')''' is executed,
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 '''%bunny = new('Ibizan hound')''' &mdash; there are
instantiated by <code>%bunny = new('Ibizan hound')</code> &mdash; 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:
<pre>
 
%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')
</pre>
</p>


A reference to an object can be inside another object:
A reference to an object can be inside another object:
<pre>
 
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')
</pre>
</p>


In the above example, the object created by
In the above example, the object created by
'''new('Mick', 'Kerry Blue Terrier')''' can be accessed
<code>new('Mick', 'Kerry Blue Terrier')</code> can be accessed
via '''%first:nextCompetitor''', so it will not be implicitly
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:
<pre>
 
%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
</pre>
</p>
 
In the above example, the object created by
In the above example, the object created by
'''new('Les', 'Pekingese')''' can be accessed by
<code>new('Les', 'Pekingese')</code> can be accessed by
'''%first:nextCompetitor:nextCompetitor''', so it is not implicitly
<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:
<pre>
 
%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
</pre>
</p>
 
A cycle can even be created with a single object:
A cycle can even be created with a single object:
<pre>
 
%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
</pre>
</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:
<pre>
 
%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
</pre>
</p>
Such objects will not be discarded by the [[Janus SOAP User Language Interface]] until the user logs off.
 
Most object-oriented languages solve this problem with [[Managing object storage|garbage collection]] &mdash; a process in which all objects are scanned to determine if they are "reachable" from some base variables (in the [[Janus SOAP User Language Interface]] these would be local and common %variables).
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]] &mdash; 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.


[[Janus SOAP User Language Interface]] has a DeepDiscard method, which provides an alternative approach to
==Deep discard of objects==
some of these cases that is less expensive than [[Managing object storage|garbage collection]]
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
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.
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 '''%chain''' contains
The object referenced by <code>%chain</code> contains
a reference to another object.
a reference to another object.
When '''%chain''' is discarded, the reference to the object with the name
When <code>%chain</code> is discarded, the reference to the object with the name
"Frack" is lost.
<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.
<pre>
 
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
</pre>
</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:
<pre>
 
%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
</pre>
</p>
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.


[[Janus SOAP User Language Interface]] also has a generic '''DeepDiscard''' method:
In this case, even when the object referenced by <code>%chain</code> is discarded,
<pre>
and its reference to the <code>Frack</code> object goes away, there is still a reference to
%obj:DeepDiscard
the <code>Frack</code> object, namely, <code>%link</code>, so that object is not discarded.
</pre>
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
<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 objects#Declaration blocks|Disallow Discard clause]]
<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===


==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 refered was
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 null value cannot be used as
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 null:
whether the object is <var>Null</var>:
<pre>
 
%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
</pre>
</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:
<pre>
<p class="code">%first  is object dog
%first  is object dog
%new    is object dog
%new    is object dog
   
   
%new  = %first
%new  = %first
</pre>
</p>
 
And a variable can be explicitly set to Null:
And a variable can be explicitly set to Null:
<pre>
<p class="code">%winner  is object dog
%winner  is object dog
  ...
  ...
%winner = null
%winner = null
</pre>
</p>


In both comparisons and assignments, '''Null''' is actually a shorthand for a [[Shared class members|shared]] null property on the class, and it can be indicated as such:
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:
<pre>
<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
  ...
  ...
</pre>
</p>
The value '''Null''' can also be passed as an input parameter to
The value <var>Null</var> can also be passed as an input parameter to
a method:
a method:
<pre>
<p class="code">class show
class show
   public
   public
       ...
       ...
Line 768: Line 790:
%minScore = %akc:score(null)
%minScore = %akc:score(null)
  ...
  ...
</pre>
</p>


:note.
<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.
:enote.
</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>
<pre>
<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
</pre>
</p>
In the above example, both '''%first''' and '''%new''' are
In the above example, both <code>%first</code> and <code>%new</code> are
set to Null after the '''%first:discard'''.
set to <var>Null</var> after the <code>%first:discard</code>.
===Passing object variables as parameters===


==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:
<pre>
<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)
</pre>
</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:
<pre>
<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
</pre>
</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:
<pre>
 
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)
</pre>
</p>
In this example %schnoz would be set to reference a new instance of
 
a Nose object that was instantiated by the Honk method.
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>:
<pre>
<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)
</pre>
</p>
:note
 
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.
:enote.
 
.
This can happen directly:
This can happen directly:
<pre>
 
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
</pre>
</p>


Or, it can happen indirectly:
Or, it can happen indirectly:
<pre>
 
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
</pre>
</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:

  1. Objects can get quite large, and moving them around an application rather than moving references around would be extremely expensive.
  2. 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

See also