Skip to main content
Previous sectionNext section

Classes

This chapter discusses the basic rules for defining and working with classes in InterSystems IRIS®.

The next chapter discusses objects and object classes. Namespaces are discussed later in this book.

Class Names and Packages

Each InterSystems IRIS class has a name, which must be unique within the namespace where it is defined. A full class name is a string delimited by one or more periods, as in the following example: package.subpackage.subpackage.class. The short class name is the part after the final period within this string; the part preceding the final period is the package name.

The package name is simply a string, but if it contains periods, the InterSystems IRIS development tools treat each period-delimited piece as a subpackage. Atelier and other tools display these subpackages as a hierarchy of folders, for convenience.

Basic Contents of a Class Definition

An InterSystems IRIS class definition can include the following items, all known as class members:

  • Parameters — A parameter defines a constant value for use by this class. The value is set at compilation time.

  • Methods — There are two kinds of methods: instance methods and class methods (called static methods in other languages). In most cases, a method is a subroutine.

  • Properties — A property contains data for an instance of the class.

  • Class queries — A class query defines an SQL query that can be used by the class and specifies a class to use as a container for the query.

  • XData blocks — An XData block is a well-formed XML document within the class, for use by the class.

    These have many possible applications.

  • Other kinds of class members that are relevant only for persistent classes; these are discussed in the next chapter.

A class definition can include keywords; these affect the behavior of the class compiler. You can specify some keywords for the entire class, and others for specific class members. These keywords affect the code that the class compiler generates and thus control the behavior of the class.

The following shows a simple InterSystems IRIS class definition:

Class MyApp.Main.SampleClass Extends %RegisteredObject
{

Parameter CONSTANTMESSAGE [Internal] = "Hello world!" ;

Property VariableMessage As %String [ InitialExpression = "How are you?"];

Property MessageCount As %Numeric [Required];

ClassMethod HelloWorld() As %String 
 {
    Set x=..#CONSTANTMESSAGE
    Quit x
 }

Method WriteIt() [ ServerOnly = 1]
{
    Set count=..MessageCount
    For i=1:1:count {
        Write !,..#CONSTANTMESSAGE," ",..VariableMessage
        }
    }

}
Copy code to clipboard

Note the following points:

  • The first line gives the name of the class. MyApp.Main.SampleClass is the full class name, MyApp.Main is the package name, and SampleClass is the short class name.

    Atelier and other user interfaces treat each package as a folder. For example:

  • Extends is a compiler keyword.

    The Extends keyword specifies that this class is a superclass of %RegisteredObject, which is a system class, discussed in the next chapter. This class extends only one class, but it is possible to extend multiple other classes. Those classes, in turn, can extend other classes.

  • CONSTANTMESSAGE is a parameter. By convention, all parameters in InterSystems IRIS system classes have names in all capitals. This is a convenient convention, but you are not required to follow it.

    The Internal keyword is a compiler keyword. It marks this parameter as internal, which suppresses it from display in the class documentation. This parameter has a string value.

  • VariableMessage and MessageCount are properties. The item after As indicates the types for these properties. InitialExpression and Required are compiler keywords.

  • HelloWorld() is a class method and it returns a string; this is indicated by the item after As.

    This method uses the value of the class parameter.

  • WriteIt() is an instance method and it does not return a value.

    This method uses the value of the class parameter and values of two properties.

    The ServerOnly compiler keyword means that this method will not be projected to Java or C++ clients.

The following Terminal session shows how we can use this class:

TESTNAMESPACE>write ##class(MyApp.Main.SampleClass).HelloWorld()
Hello world!
TESTNAMESPACE>set x=##class(MyApp.Main.SampleClass).%New()
 
TESTNAMESPACE>set x.MessageCount=3
 
TESTNAMESPACE>do x.WriteIt()
 
Hello world! How are you?
Hello world! How are you?
Hello world! How are you?
Copy code to clipboard

Class Name Shortcuts

When referring to a class, you can omit the package (or the higher level packages) in the following scenarios:

  • The reference is within a class, and the referenced class is in the same package or subpackage.

  • The reference is within a class, and the class uses the IMPORT directive to import the package or subpackage that contains the referenced class.

  • The reference is within a method, and the method uses the IMPORT directive to import the package or subpackage that contains the referenced class.

  • You are referring to a class in the %Library package, which is specially handled. You can refer to the class %Library.ClassName as %ClassName. For example, you can refer to %Library.String as %String.

  • You are referring to a class in the User package, which is specially handled. For example, you can refer to User.MyClass as MyClass.

    InterSystems does not provide any classes in the User package, which is reserved for your use.

Class Parameters

A class parameter defines a value that is the same for all objects of a given class. This value is established when the class is compiled and cannot be altered at runtime. You use class parameters for the following purposes:

  • To define a value that should not be changed at runtime.

  • To define user-specific information about a class definition. A class parameter is simply an arbitrary name-value pair; you can use it to store any information you like about a class.

  • To customize the behavior of the various data type classes (such as providing validation information) when used as properties; this is discussed in the next section.

  • To provide parameterized values for method generator methods to use.

The following shows a class with several parameters:

Class GSOP.DivideWS Extends %SOAP.WebService
{

Parameter USECLASSNAMESPACES = 1;

///  Name of the Web service.
Parameter SERVICENAME = "Divide";

///  SOAP namespace for the Web service
Parameter NAMESPACE = "http://www.mynamespace.org";

/// let this Web service understand only SOAP 1.2
Parameter SOAPVERSION = "1.2";

 ///further details omitted
}
Copy code to clipboard

Properties

Formally, there are two kinds of properties in InterSystems IRIS:

  • Attributes, which hold values. The value can be any of the following:

    • A single, literal value, usually based on a data type.

    • An object value (this includes collection objects and stream objects, both introduced in the next chapter).

    • A multidimensional array. This is less common.

    The word property often refers just to properties that are attributes, rather than properties that hold associations.

  • Relationships, which hold associations between objects.

This section shows a sample class that contains property definitions that show some of these variations:

Class MyApp.Main.Patient Extends %Persistent
{

Property PatientID As %String [Required];

Property Gender As %String(DISPLAYLIST = ",Female,Male", VALUELIST = ",F,M");

Property BirthDate As %Date;

Property Age As %Numeric [Transient];

Property MyTempArray [MultiDimensional];

Property PrimaryCarePhysician As Doctor;

Property Allergies As list Of PatientAllergy;

Relationship Diagnoses As PatientDiagnosis [ Cardinality = children, Inverse = Patient ]; 
}
Copy code to clipboard

Note the following:

  • In each definition, the item after As is the type of the property. Each type is a class. The syntax As List Of is shorthand for a specific collection class; these are discussed in the next chapter.

    %String, %Date, and %Numeric are data type classes.

    %String is the default type.

  • Diagnoses is a relationship property; the rest are attribute properties.

  • PatientID, Gender, BirthDate, and Age can contain only simple, literal values.

  • PatientID is required because it uses the Required keyword. This means that you cannot save an object of this class if you do not specify a value for this property.

  • Age is not saved to disk, unlike the other literal properties. This is because it uses the Transient keyword.

  • MyTempArray is a multidimensional property because it uses the MultiDimensional keyword. This property is not saved to disk by default.

  • PrimaryCarePhysician and Allergies are object-valued properties.

  • The Gender property definition includes values for property parameters. These are parameters in the data type class that this property uses.

    This property is restricted to the values M and F. When you display the logical values (as in the Management Portal), you see Male and Female instead. Each data type class provides methods such as LogicalToDisplay().

Object-valued properties and relationship properties are discussed in the next chapter.

Specifying Property Keywords

In a property definition, you can include optional property keywords that affect how the property is used. The following list shows some of the most commonly seen keywords:

Specifies that the value of the property set before an instance of this class can be stored to disk. By default, properties are not required. In a subclass, you can mark an optional property as required, but you cannot do the reverse.

Specifies an initial value for the property. By default, properties have no initial value. Subclasses inherit the value of the InitialExpression keyword and can override it. The value specified must be a valid ObjectScript expression (this applies even if the class is written in another language).

Specifies that the property is not stored in the database. By default, properties are not transient. Subclasses inherit the value of the Transient keyword and cannot override it.

Specifies that the property is private. Subclasses inherit the value of the Private keyword and cannot override it.

By default, properties are public and can be accessed anywhere. You can mark a property as private (via the Private keyword). If so, it can only be accessed by methods of the object to which it belongs.

In InterSystems IRIS, private properties are always inherited and visible to subclasses of the class that defines the property.

In other programming languages, these are often called protected properties.

Specifies that the property has no in-memory storage allocated for it when the object containing it is instantiated. By default, a property is not calculated. Subclasses inherit the Calculated keyword and cannot override it.

Specifies that the property is multidimensional. This property is different from other properties as follows:

  • It does not have associated methods (see the following topics).

  • It is ignored when the object is validated or saved.

  • It is not saved to disk, unless your application includes code to save it specifically.

  • It cannot be exposed to client technologies.

  • It cannot be stored in or exposed through SQL tables.

Multidimensional properties are rare but are occasionally useful to temporarily contain object state information.

Properties Based on Data Types

When you define a property and you specify its type as a data type class, you have special options for defining and working with that property, as described in this section.

Data Type Classes

Data type classes enable you to enforce sets of rules about the values of properties.

InterSystems IRIS provides data type classes which include %Library.String, %Library.Integer, %Library.Numeric, %Library.Date, and many others. Because the names of classes of the %Library package can be abbreviated, you can abbreviate many of these; for example, %Date is an abbreviation for %Library.Date.

Each data type class has the following features:

  • It specifies values for compiler keywords. For a property, a compiler keyword can do things like the following:

    • Make the property required

    • Specify an initial value for the property

    • Control how the property is projected to SQL, ODBC, and Java clients

  • It specifies values for parameters that affect the details such as the following:

    • Maximum and minimum allowed logical value for the data type

    • Maximum and minimum number of characters the string can contain

    • Number of digits following the decimal point

    • Whether to truncate the string if it exceeds the maximum number of characters

    • Display format

    • How to escape any special XML or HTML characters

    • Enumerated lists of logical values and display values to use in any user interface

    • Pattern that the string must match (automatically uses the InterSystems IRIS pattern-matching operator)

    • Whether to respect or ignore the UTC time zone when importing or exporting to XML

  • It provides a set of methods to translate literal data among the stored (on disk), logical (in memory), and display formats.

You can add your own data type classes. For example, the following shows a custom subclass of %Char:

Class MyApp.MyType Extends %Library.Char
{

/// The maximum number of characters the string can contain.
Parameter MAXLEN As INTEGER = 2000;

}
Copy code to clipboard

Overriding Parameters of Data Type Classes

When you define a property and you specify its type as a data type class, you can override any parameters defined by the data type class.

For example, the %Integer data type class defines the class parameter (MAXVAL) but provides no value for this parameter. You can override this in a property definition as follows:

Property MyInteger As %Integer(MAXVAL=10);
Copy code to clipboard

For this property, the maximum allowed value is 10.

(Internally, this works because the validation methods for the data type classes are method generators; the parameter value you provide is used when the compiler generates code for your class. Method generators are discussed later in “Special Kinds of Methods.”)

Similarly, every property of type %String has a collation type, which determines how values are ordered (such as whether capitalization has effects or not). The default collation type is SQLUPPER. For more details on collations, see the section “Data Collation” in the chapter “InterSystems IRIS SQL Basics” in Using InterSystems SQL.

For another example, the data type classes define the DISPLAYLIST and VALUELIST parameters, which you can use to specify choices to display in a user interface and their corresponding internal values:

Property Gender As %String(DISPLAYLIST = ",Female,Male", VALUELIST = ",F,M");
Copy code to clipboard

Using Instance Variables

To access the in-memory value of a property from within an instance method of an object, you can use the following in-memory value syntax:

 Set i%Name = "Carl"
Copy code to clipboard

This directly sets “Carl” as the in-memory value of the property Name, bypassing the NameSet accessor method (if present). The variable i%Name is an instance variable; see “i%<PropertyName> syntax” in Defining and Using Classes. For information on accessor methods, see see the chapter “Using and Overriding Property Methods” in the same book.

Using Other Property Methods

Properties have a number of methods associated with them automatically. These methods are generated by the data type classes.

For example, if we define a class Person with three properties:

Class MyApp.Person Extends %Persistent
{
Property Name As %String;
Property Age As %Integer;
Property DOB As %Date;
}
Copy code to clipboard

The names of each generated method is the property name concatenated with the name of the method from the inherited class. For example, some of the methods associated with the DOB property are:

 Set x = person.DOBIsValid(person.DOB)
 Write person.DOBLogicalToDisplay(person.DOB)
Copy code to clipboard

where IsValid is a method of the property class and LogicalToDisplay is a method of the %Date data type class.

Methods

There are two kinds of methods: instance methods and class methods (called static methods in other languages). In most cases, a method is a procedure.

Specifying Method Keywords

In a method definition, you can include optional compiler keywords that affect how the method behaves. The following list shows some of the most commonly seen method keywords:

By default, the variables used in a method are private to that method, because by default all methods are procedure blocks. To define a method as a non-procedure block, specify the ProcedureBlock keyword as 0. For example:

Method MyMethod() [ ProcedureBlock = 0 ] 
{
    //implementation details
}
Copy code to clipboard

In this case, the variables in this method would be public variables.

Specifies that the method is private. Subclasses inherit the value of the Private keyword and cannot override it.

By default, methods are public and can be accessed anywhere. You can mark a method as private (via the Private keyword). If you do:

  • It can only be accessed by methods of the class to which it belongs.

  • It does not appear in the InterSystems Class Reference.

It is, however, inherited and available in subclasses of the class that defines the method.

Other languages often call such methods protected methods.

References to Other Class Members

Within a method, use the syntax shown here to refer to other class members:

  • To refer to a parameter, use an expression like this:

     ..#PARAMETERNAME
    Copy code to clipboard

    In classes provided by InterSystems, all parameters are defined in all capitals, by convention, but your code is not required to do this.

  • To refer to another method, use an expression like this:

    ..methodname(arguments)
    Copy code to clipboard

    Note that you cannot use this syntax within a class method to refer to an instance method.

  • (Within an instance method only) To refer to a property of the instance, use an expression like this:

    ..PropertyName
    Copy code to clipboard

    Similarly, to refer to a property of an object-valued property, use an expression like this:

    ..PropertyNameA.PropertyNameB
    Copy code to clipboard

    This is known as InterSystems IRIS dot syntax.

    Also, you can invoke an instance method of an object-valued property. For example:

     do ..PropertyName.MyMethod()
    Copy code to clipboard

References to Methods of Other Classes

Within a method (or within a routine), use the syntax shown here to refer to a method in some other class:

  • To invoke a class method and access its return value, use an expression like the following:

    ##class(Package.Class).MethodName(arguments)
    Copy code to clipboard

    For example:

     Set x=##class(Util.Utils).GetToday()
    Copy code to clipboard

    Or, if you are not interested in the return value, use DO as follows:

     Do ##class(Util.Utils).DumpValues()
    Copy code to clipboard
    Note:

    ##class is not case-sensitive.

  • To invoke an instance method, create an instance (as described in the next chapter) and then use an expression like the following to invoke the method and access its return value:

    instance.MethodName(arguments)
    Copy code to clipboard

    For example:

     Set x=instance.GetName()
    Copy code to clipboard

    Or, if you are not interested in the return value, use DO as follows:

     Do instance.InsertItem("abc")
    Copy code to clipboard

Not all methods have return values, so choose the syntax appropriate for your case.

References to Current Instance

Within an instance method, sometimes it is necessary to refer to the current instance itself, rather than to a property of method of the instance. For example, you might need to pass the current instance as an argument when invoking some other code. In such a case, use the special variable $THIS to refer to the current instance.

For example:

 Set sc=header.ProcessService($this)
Copy code to clipboard

Method Arguments

A method can take positional arguments in a comma-separated list. For each argument, you can specify a type and the default value.

For instance, here is the partial definition of a method that takes three arguments:

Method Calculate(count As %Integer, name, state As %String = "CA") as %Numeric
{
    // ...
}
Copy code to clipboard

Notice that two of the arguments have explicit types, and one has an default value. Generally it is a good idea to explicitly specify the type of each argument.

Skipping Arguments

In ObjectScript, when you invoke a method, you can skip arguments, if there are suitable defaults for them. For example, the following is valid:

 set myval=##class(mypackage.myclass).GetValue(,,,,,,4)
Copy code to clipboard

Passing Variables by Value or by Reference

When you invoke a method, you can pass values of variables to that method either by value or by reference, in just the same way that you do with routines and subroutines; see “Passing Variables by Value or by Reference,” earlier in this book:

The signature of a method usually indicates whether you are intended to pass arguments by reference. For example:

Method MyMethod(argument1, ByRef argument2, Output argument3)

The ByRef keyword indicates that you should pass this argument by reference. The Output keyword indicates that you should pass this argument by reference and that the method ignores any value that you initially give to this argument.

Similarly, when you define a method, you use the ByRef and Output keywords in the method signature to inform other users of the method how it is meant to be used.

Important:

The ByRef and Output keywords provide information for the benefit of anyone using the InterSystems Class Reference. They do not affect the behavior of the code. It is the responsibility of the writer of the method to enforce any rules about how the method is to be invoked.

Variable Numbers of Arguments

You can define a method so that it accepts a variable number of arguments. For example:

ClassMethod MultiArg(Arg1... As %List)
{
 Write "Invocation has ",
     $GET(Arg1, 0),
     " element",
     $SELECT(($GET(Arg1, 0)=1):"", 1:"s"),
     !
 For i = 1 : 1 : $GET(Arg1, 0)
 {
     Write:($DATA(Arg1(i))>0) "Argument[", i , "]:", 
         ?15, $GET(Arg1(i), "<NULL>"), !
 }
 Quit
}
Copy code to clipboard

Because methods are procedures, they support the ... syntax to accept variable numbers of arguments. This syntax is described in the “Variable Numbers of Arguments” section of the “User-defined Code” chapter of Using ObjectScript.

Specifying Default Values

To specify an argument’s default value, use syntax as shown in the following example:

Method Test(flag As %Integer = 0)
{
 //method details
}
Copy code to clipboard

When a method is invoked, it uses its default values (if specified) for any missing arguments.

Another option is to use the $GET function. For example:

Method Test(flag As %Integer)
{
  set flag=$GET(flag,0)
 //method details
}
Copy code to clipboard

This technique, however, does not affect the class signature.

Special Kinds of Methods

The CodeMode keyword enables you to define other, special kinds of methods:

Call Methods

A call method is a special mechanism to create method wrappers around existing InterSystems IRIS routines. The syntax for a call method is as follows:

Method Call() [ CodeMode = call ]
{
    Label^Routine
}
Copy code to clipboard

where “Label^Routine” specifies a label within a routine.

Method Generators

A method generator is a program that is invoked by the class compiler during class compilation. Its output is the actual runtime implementation of the method. Method generators provide a means of inheriting methods that can produce high performance, specialized code that is customized to the needs of the inheriting class or property. Within the InterSystems IRIS library, method generators are used extensively by the data type and storage classes.

For details, refer to the “Method Generators” chapter of Using ObjectScript.

Class Queries

An InterSystems IRIS class can contain class queries. A class query defines an SQL query that can be used by the class and specifies a class to use as a container for the query. The following shows an example:

Query QueryName(Parameter As %String) As %SQLQuery
{
SELECT MyProperty, MyOtherProperty FROM MyClass
 WHERE (MyProperty = "Hello" AND MyOtherProperty = :Parameter)
 ORDER BY MyProperty
}
Copy code to clipboard

You define class queries to provide predefined lookups for use in your application. For example, you can look up instances by some property, such as by name, or provide a list of instances that meet a particular set of conditions, such as all the flights from Paris to Madrid. The example shown here uses a parameter, which is a common way to provide a flexible query. Note that you can define class queries within any class; there is no requirement to include class queries within persistent classes, which are introduced later in this book.

XData Blocks

Because XML is often a useful way to represent structured data, InterSystems IRIS classes include a mechanism that allow you to include well-formed XML documents, for any need you might have. To do this, you include an XData block, which is another kind of class member.

InterSystems IRIS uses XData blocks for certain specific purposes, and these might give you ideas for your own applications:

  • WS-Policy support for InterSystems IRIS web service services and web clients. See Creating Web Services and Web Clients. In this case, an XData block describes the security policy.

  • In Business Intelligence, you use XData blocks to define cubes, subject areas, KPIs, and other elements.

Macros and Include Files in Class Definitions

In an InterSystems IRIS class definition, you can define macros in a method and use them in that method. More often, however, you define them in an include file, which you can include at the start of any class definition. For example:

Include (%assert, %callout, %occInclude, %occSAX)

/// Implements an interface to the XSLT Parser. XML contained in a file or binary stream 
/// may be transformed
Class %XML.XSLT.Transformer Extends %RegisteredObject ...
Copy code to clipboard

Then your methods in that class can refer to any macros defined in that include file, or in its included include files.

Macros are inherited. That is, a subclass has access to all the same macros as its superclasses.

Inheritance Rules in InterSystems IRIS

As with other class-based languages, you can combine multiple class definitions via inheritance. An InterSystems IRIS class definition can extend (or inherit from) multiple other classes. Those classes, in turn, can extend other classes.

The following subsections provide the basic rules for inheritance of classes in InterSystems IRIS.

Inheritance Order

InterSystems IRIS uses the following rules for inheritance order:

  1. By default, if a class member of a given name is defined in multiple superclasses, the subclass takes the definition from the left-most class in the superclass list.

  2. If the class definition contains Inheritance = right, then the subclass takes the definition from the right-most class in the superclass list.

    For reasons of history, most InterSystems IRIS classes contain Inheritance = right.

Primary Superclass

Any class that extends other classes has a single primary superclass.

No matter which inheritance order a class uses, the primary superclass is the first one, reading left to right.

For any class-level compiler keywords, a given class uses the values specified in its primary superclass.

For a persistent class, the primary superclass is especially important; see “Classes and Extents,” later in this book.

Most-Specific Type Class

Although an object can be an instance belonging to the extents of more than one class — such as that of various superclasses — it always has a most-specific type class (MSTC). A class is the most specific type of an object when that object is an instance of that class, but is not an instance of any subclass of that class.

Overriding Methods

A class inherits methods (both class and instance methods) from its superclass or superclasses, which you can override. If you do so, you must ensure that the signature in your method definition matches the signature of the method you are overriding. This even includes that any argument of a subclass’s method cannot have a data type specified if the matching argument of the superclass’s method has no data type specified. The method in the subclass can, however, specify additional arguments that are not defined in the superclass.

Within a method in a subclass, you can refer to the method that it overrides in a superclass. To do so, use the ##super() syntax. For example:

//overrides method inherited from a superclass
Method MyMethod() 
{
  //execute MyMethod as implemented in the superclass
  do ##super()
  //do more things....
}
Copy code to clipboard
Note:

##super is not case-sensitive.

For More Information

For more information on the topics covered in this chapter, see the following books:

  • 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.