Skip to main content

Objects

This page discusses objects in InterSystems IRIS® data platform.

The code samples shown in this page use classes from the Samples-Data sample (https://github.com/intersystems/Samples-DataOpens in a new tab). InterSystems recommends that you create a dedicated namespace called SAMPLES (for example) and load samples into that namespace. For the general process, see Downloading Samples for Use with InterSystems IRIS.

Introduction to InterSystems IRIS Object Classes

InterSystems IRIS provides object technology by means of the following object classes: %Library.RegisteredObjectOpens in a new tab, %Library.PersistentOpens in a new tab, and %Library.SerialObjectOpens in a new tab.

The following figure shows the inheritance relationships among these classes, as well as some of their parameters and methods. The names of classes of the %Library package can be abbreviated, so that (for example) %PersistentOpens in a new tab is an abbreviation for %Library.PersistentOpens in a new tab. Here, the items in all capitals are parameters and the items that start with percent signs are methods.

The classes %Persistent and %SerialObject inherit the parameters and methods of the parent class %RegisteredObject.

In a typical class-based application, you define classes based on these classes (and on specialized system subclasses). All objects inherit directly or indirectly from one of these classes, and every object is one of the following types:

  • A registered object is an instance of %RegisteredObjectOpens in a new tab or a subclass. You can create these objects but you cannot save them. The other two classes inherit from %RegisteredObjectOpens in a new tab and thus include all the parameters, methods, and so on of that class.

  • A persistent object is an instance of %PersistentOpens in a new tab or a subclass. You can create, save, open, and delete these objects.

    A persistent class is automatically projected to a table that you can access via InterSystems SQL.

  • A serial object is an instance of %SerialObjectOpens in a new tab or a subclass. A serial class is meant for use as a property of another object. You can create these objects, but you cannot save them or open them independently of the object that contains them.

    When contained in persistent objects, these objects have an automatic projection to SQL.

Note:

Via the classes %DynamicObjectOpens in a new tab and %DynamicArrayOpens in a new tab, InterSystems IRIS also provides the ability to work with objects and arrays that have no schema; for details, see Using JSON.

Basic Features of Object Classes

With the object classes, you can perform the following tasks, among others:

  • You can create an object (an instance of a class). To do so, you use the %New() method of that class, which it inherits from %RegisteredObjectOpens in a new tab.

    For example:

     set myobj=##class(Sample.Person).%New()
    myobj = iris.cls("Sample.Person")._New()
    

    Python method names cannot include a percent sign (%). You can call any ObjectScript method that contains the % character from Python by replacing it with an underscore (_), as in the example.

  • You can use properties.

    You can define properties in any class, but they are useful only in object classes, because only these classes enable you to create instances.

    Any property contains a single literal value, an object (possibly a collection object), or a multidimensional array (rare). The following example shows the definition of an object-valued property:

    Property Home As Sample.Address;

    Sample.Address is another class. The following shows one way to set the value of the Home property:

     Set myaddress=##class(Sample.Address).%New()
     Set myaddress.City="Louisville"
     Set myaddress.Street="15 Winding Way"
     Set myaddress.State="Georgia"
    
     Set myperson=##class(Sample.Person).%New()
     Set myperson.Home=myaddress
    
    import iris
    myaddress=iris.cls("Sample.Address")._New()
    myaddress.City="Louisville"
    myaddress.Street="15 Winding Way"
    myaddress.State="Georgia"
    
    myperson=iris.cls("Sample.Person")._New()
    myperson.Home=myaddress
    
  • You can invoke methods of an instance of the class, if the class or its superclasses define instance methods. For example:

    Method PrintPerson() [ Language = objectscript ]
    {
     Write !, "Name: ", ..Name
    }
    
    Method PrintPerson() [ Language = python ]
    {
        print("\nName:", self.Name)
    }
    

    If myobj is an instance of the class that defines this method, you can invoke this method as follows:

     Do myobj.PrintPerson()
    myobj.PrintPerson()
    
  • You can validate that the property values comply with the rules given in the property definitions.

    • All objects inherit the instance method %NormalizeObject(), which normalizes all the object's property values. Many data types allow different representations of the same value. Normalization converts a value to its canonical, or normalized, form. %NormalizeObject() returns true or false depending on the success of this operation.

    • All objects inherit the instance method %ValidateObject(), which returns true or false depending on whether the property values comply with the property definitions.

    • All persistent objects inherit the instance method %Save(). When you use the %Save() instance method, the system automatically calls %ValidateObject() first.

    In contrast, when you work at the routine level and do not use classes, your code must include logic to check the type and other input requirements.

  • You can define callback methods to add additional custom behavior when objects are created, modified, and so on.

    For example, to create an instance of a class, you invoke the %New() method of that class. If that class defines the %OnNew() method (a callback method), then InterSystems IRIS automatically also calls that method. The following shows a simple example:

    Method %OnNew() As %Status 
    {
        Write "hi there"
        Return $$$OK
    }
    Method %OnNew() As %Status [ Language = python ]
    {
        print("hi there")
        return True
    }
    

    In realistic scenarios, this callback might perform some required initialization. It could also perform logging by writing to a file or perhaps to a global.

OREFs

The %New() method of an object class creates an internal, in-memory structure to contain the object’s data and returns an OREF (object reference) that points to that structure. An OREF is a special kind of value in InterSystems IRIS. You should remember the following points:

  • In the Terminal, the content of an OREF depends on the language in use:

    • In ObjectScript, you see a string that consists of a number, followed by an at sign (@), followed by the name of the class.

    • In Python, you see a string containing the class name and an 18 character unique location in memory.

    For example:

    TESTNAMESPACE>set myobj=##class(Sample.Person).%New()
     
    TESTNAMESPACE>w myobj
    3@Sample.Person 
    
    >>> myobj=iris.cls("Sample.Person")._New()
    >>> print(myobj)
    <iris.Sample.Person object at 0x000001A1E52FFD20>
    
  • InterSystems IRIS returns an error if you do not use an OREF where one is expected or you use one with an incorrect type. This error is different from the ObjectScript terminal and the Python terminal:

    TESTNAMESPACE>set x=2
    
    TESTNAMESPACE>set x.Name="Fred Parker"
    
    SET x.Name="Fred Parker"
    ^
    <INVALID OREF>
    
    >>> x=2
    >>> x.Name="Fred Parker"
    Traceback (most recent call last):
      File "<input>", line 1, in <module>
    AttributeError: 'int' object has no attribute 'Name'
    

    It is helpful to be able to recognize this error. It means that the variable is not an OREF but should be.

  • There is only one way to create an OREF: use a method that returns an OREF. The methods that return OREFs are defined in the object classes or their subclasses.

    The following does not create an OREF, but rather a string that looks like an OREF:

    TESTNAMESPACE>set testthis="4@Sample.Person"
    
    >>> testthis="<iris.Sample.Person object at 0x000001A1E52FFD20>"
    
  • In ObjectScript, you can determine programmatically whether a variable contains an OREF. The function $IsObject returns 1 (true) if the variable contains an OREF; and it returns 0 (false) otherwise.

Note:

For persistent classes, methods such as %OpenId() also return OREFs.

Stream Interface Classes

InterSystems IRIS allocates a fixed amount of space to hold the results of string operations. If a string expression exceeds the amount of space allocated, a <MAXSTRING> error results; see string length limit.

If you need to pass a value whose length exceeds this limit, or you need a property whose value might exceed this limit, you use a stream. A stream is an object that can contain a single value whose size is larger than the string size limit. (Internally InterSystems IRIS creates and uses a temporary global to avoid the memory limitation.)

You can use stream fields with InterSystems SQL, with some restrictions. For details and a more complete introduction, see Defining and Using Classes; also see the InterSystems Class Reference for these classes.

Note:

You cannot use ObjectScript stream fields with Python.

Stream Classes

The main InterSystems IRIS stream classes use a common stream interface defined by the %Stream.ObjectOpens in a new tab class. You typically use streams as properties of other objects, and you save those objects. Stream data may be stored in either an external file or an InterSystems IRIS global, depending on the class you choose:

To work with a stream object, you use its methods. For example, you use the Write() method of these classes to add data to a stream, and you use Read() to read data from it. The stream interface includes other methods such as Rewind() and MoveTo().

Example

For example, the following code creates a global character stream and writes some data into it:

 Set mystream=##class(%Stream.GlobalCharacter).%New()
 Do mystream.Write("here is some text to store in the stream ")
 Do mystream.Write("here is some more text")
 Write "this stream has this many characters: ",mystream.Size,!
 Write "this stream has the following contents: ",!
 Write mystream.Read()

Collection Classes

When you need a container for sets of related values, you can use $LIST format lists and multidimensional arrays.

If you prefer to work with classes, InterSystems IRIS provides list classes and array classes; these are called collections. For more details on collections, see Working with Collections.

List and Array Classes for Use As Standalone Objects

To create list objects, you can use the following classes:

Elements in a list are ordered sequentially. Their positions in a list can be accessed using integer key ranging from 1 to N, where N is the position of the last element.

To manipulate a list object, use its methods. For example:

 set Colors = ##class(%Library.ListOfDataTypes).%New()
 do Colors.Insert("Red")
 do Colors.Insert("Green")
 do Colors.Insert("Blue")

 write "Number of list items: ", Colors.Count()
 write !, "Second list item: ", Colors.GetAt(2)

 do Colors.SetAt("Yellow",2)
 write !, "New second item: ", Colors.GetAt(2)

 write !, "Third item before insertion: ", Colors.GetAt(3)
 do Colors.InsertAt("Purple",3)
 write !, "Number of items after insertion: ", Colors.Count()
 write !, "Third item after insertion: ", Colors.GetAt(3)
 write !, "Fourth item after insertion: ", Colors.GetAt(4)

 do Colors.RemoveAt(3)
 write "Number of items after removing item 3: ", Colors.Count()

 write "List items:"
 for i = 1:1:Colors.Count() write Colors.GetAt(i),!
import iris

Colors=iris.cls("%Library.ListOfDataTypes")._New()
Colors.Insert("Red")
Colors.Insert("Green")
Colors.Insert("Blue")

print("Number of list items:", Colors.Count())
print("Second list item:", Colors.GetAt(2))

Colors.SetAt("Yellow",2)
print("New second item: ", Colors.GetAt(2))

print("Third item before insertion: ", Colors.GetAt(3))
Colors.InsertAt("Purple",3)
print("Number of items after insertion: ", Colors.Count())
print("Third item after insertion: ", Colors.GetAt(3))
print("Fourth item after insertion: ", Colors.GetAt(4))

Colors.RemoveAt(3)
print("Number of items after removing item 3: ", Colors.Count())

print("List items:")
for i in range(1, Colors.Count() + 1) print(Colors.GetAt(i))

Similarly, to create array objects, you can use the following classes:

To manipulate an array object, use its methods. For example:

 set ItemArray = ##class(%Library.ArrayOfDataTypes).%New()
 do ItemArray.SetAt("example item","alpha")
 do ItemArray.SetAt("another item","beta")
 do ItemArray.SetAt("yet another item","gamma")
 do ItemArray.SetAt("still another item","omega")
 write "Number of items in this array: ", ItemArray.Count()
 write !, "Item that has the key gamma: ", ItemArray.GetAt("gamma")
import iris
ItemArray=iris.cls("%Library.ArrayOfDataTypes")._New()
ItemArray.SetAt("example item", "alpha")
ItemArray.SetAt("another item", "beta")
ItemArray.SetAt("yet another item", "gamma")
ItemArray.SetAt("still another item", "omega")
print("Number of items in this array:", ItemArray.Count())
print("Item that has the key gamma:", ItemArray.GetAt("gamma"))

The SetAt() method adds items to the array, where the first argument is the element to be added and the second argument is the key. Array elements are ordered by key, with numeric keys first, sorted from smallest to largest, and string keys next, sorted alphabetically with uppercase letters coming before lowercase letters. For example: -2, -1, 0, 1, 2, A, AA, AB, a, aa, ab.

List and Arrays as Properties

You can also define a property as a list or array.

To define a property as a list, use the following form:

Property MyProperty as list of Classname;

If Classname is a data type class, then InterSystems IRIS uses the interface provided by %Collection.ListOfDTOpens in a new tab. If Classname is an object class, then it uses the interface provided by %Collection.ListOfObjOpens in a new tab.

To define a property as an array, use the following form:

Property MyProperty as array of Classname;

If Classname is a data type class, then InterSystems IRIS uses the interface provided by %Collection.ArrayOfDTOpens in a new tab. If Classname is an object class, then it uses the interface provided by %Collection.ArrayOfObjOpens in a new tab.

Useful ObjectScript Functions

ObjectScript provides the following functions for use with object classes:

  • $CLASSMETHOD enables you to run a class method, given as class name and method name. For example:

    TESTNAMESPACE>set class="Sample.Person"
     
    TESTNAMESPACE>set obj=$CLASSMETHOD(class,"%OpenId",1)
     
    TESTNAMESPACE>w obj.Name
    Van De Griek,Charlotte M.
    

    This function is useful when you need to write generic code that executes a class method, but the class name (or even the method name) is not known in advance. For example:

     //read name of class from imported document
     Set class=$list(headerElement,1) 
     // create header object
     Set headerObj=$classmethod(class,"%New")

    The other functions are useful in similar scenarios.

  • $METHOD enables you to run an instance method, given an instance and a method name. For example:

    TESTNAMESPACE>set obj=##class(Sample.Person).%OpenId(1)
     
    TESTNAMESPACE>do $METHOD(obj,"PrintPerson")
     
    Name: Van De Griek,Charlotte M.
    
  • $PROPERTY gets or sets the value of the given property for the given instance. For example:

    TESTNAMESPACE>set obj=##class(Sample.Person).%OpenId(2)
     
    TESTNAMESPACE>write $property(obj,"Name")
    Edison,Patrick J.
    
  • $PARAMETER gets the value of the given class parameter, given an instance. For example:

    TESTNAMESPACE>set obj=##class(Sample.Person).%OpenId(2)
     
    TESTNAMESPACE>write $parameter(obj,"EXTENTQUERYSPEC")
    Name,SSN,Home.City,Home.State
    
  • $CLASSNAME returns the class name for a given instance. For example:

    TESTNAMESPACE>set obj=##class(Sample.Person).%OpenId(1)
     
    TESTNAMESPACE>write $CLASSNAME(obj)
    Sample.Person
    

    With no argument, this function returns the class name of the current context. This can be useful in instance methods.

See Also

For more information on the topics covered in this page, see the following resources:

  • Defining and Using Classes describes how to define classes and class members in InterSystems IRIS.

  • Class Definition Reference provides reference information for the compiler keywords that you use in class definitions.

  • The InterSystems Class Reference provides details on all non-internal classes provided with InterSystems IRIS.

FeedbackOpens in a new tab