Objects in OpenMatrix Language
Objects in OML are defined using the classdef keyword. This creates a blueprint for an object but does not actually create (instantiate) anything.
There are three parts to defining an object:
- The declaration including the name and parent class.
- The properties section.
- The methods section.
A class defined by the classdef keyword has a name, which must be
a valid OML function
name.
classdef MyClass
end
Properties of an object act in a similar manner to
structs.
classdef MyClass
properties
name
value
end
end
By default, properties are public and can be accessed by using the dot (.)
operator.
m = MyClass;
m.name = ‘fun’;
m.value = 0;
Default values of properties can be assigned when they are
declared.
classdef MyClass
properties
name = ‘fun’;
value = 0;
end
end
m = MyClass;
Methods of an object are regular OML functions that can operate
on the object’s data. The first parameter to each method is the object
itself.
classdef MyClass
properties
name
value
end
methods
function obj = AddOne(self)
obj.value = self.value+1;
obj.name = self.name;
end
end
end
m = MyClass;
m.name = ‘fun’;
m.value = 0;
m = m.AddOne();
The constructor is a special method. It must have the same name as the class itself.
The first parameter to the constructor is an exception to the rule above in that it
is not the object itself. The constructor is expected to return a newly-created
object.
classdef MyClass
properties
name
value
end
methods
function obj = MyClass(in_name, in_value)
obj.name = in_name;
obj.value = in_value;
end
end
end
m = MyClass(‘fun’, 3);
A class can inherit properties and methods from other classes using the <
operator.
classdef Employee
properties
name;
ID;
end
methods
function obj = Employee (in_name, in_ID)
obj.name = in_name;
obj.ID = in_ID;
end
end
end
classdef Supervisor < Employee
properties
group
end
methods
function obj = Supervisor(in_name, in_ID)
obj.name = in_name;
obj.id = in_ID;
obj.group = {};
end
end
end
super = Supervisor(‘Chris’, 4)
In the previous example, instead of duplicating the Employee constructor code in the
Supervisor constructor, it is possible to call a method from the base class using
the following
syntax:
function obj = Supervisor(in_name, in_ID)
obj = obj@Employee(in_name, in_ID);
obj.group = {};
end
A special base class is the handle class. Inheriting from this predefined class
allows objects to be treated as references instead of values when passed to
functions. In this previous example, it was necessary to return a modified object
from the AddOne method and then overwrite the unmodified object with the
newly-returned
object.
classdef MyClass
properties
name
value
end
methods
function obj = AddOne(self)
obj.value = self.value+1;
obj.name = self.name;
end
end
end
m = MyClass;
m.name = ‘fun’;
m.value = 0;
m = m.AddOne()
Instead, if MyClass inherits from the handle base class, the following code gives the
same
result:
classdef MyClass < handle
properties
name
value
end
methods
function AddOne(self)
self.value = self.value+1;
end
end
end
m = MyClass;
m.name = 'fun';
m.value = 0;
m.AddOne();
m
Standard OML functions can be overloaded in a class’s methods
block.
classdef ratnum
properties
n
d
end
methods
function r = ratnum(numerator, denomenator)
r.n = numerator;
r.d = denomenator;
end
function r = sqrt(r)
r = sqrt(r.n/r.d);
end
end
end
a = ratnum(2,3);
sqrt(a)
In this case, when sqrt is called, the class’s method is used instead of the standard built-in function (which is unaware of user-defined classes).
Overloading the disp function has an additional effect. Whenever
an object is to be displayed (implicitly or not), the disp method
for that object will be called instead of using the standard struct-like
display.
classdef ratnum
properties
n
d
end
methods
function r = ratnum(numerator, denomenator)
r.n = numerator;
r.d = denomenator;
end
function disp(r)
if (r.d ~= 1)
fprintf('%d/%d\n', r.n, r.d);
else
fprintf('%d\n', r.n);
end
end
end
end
a = ratnum(2,3)
Standard OML operators can be overloaded using special method
names. For example, if a class contains a method named ‘plus’, this will be called
instead of the standard plus function when the operator + is
used.
classdef ratnum
properties
n
d
end
methods
function r = ratnum(numerator, denomenator)
r.n = numerator;
r.d = denomenator;
end
function disp(r)
if (r.d ~= 1)
fprintf('%d/%d\n', r.n, r.d);
else
fprintf('%d\n', r.n);
end
end
function plus(r1, r2)
if (class(r2) == 'ratnum')
r = ratnum(r1.n*r2.d + r2.n*r1.d, r1.d * r2.d);
elseif (isscalar(r2))
r = ratnum(r1.d*r2 + r1.n, r1.d);
else
r = 0;
end
end
end
end
ratnum(1,3)+ratnum(1,2)
Note: The plus function is called even though it is the +
operator that is used.
The first parameter to any overloaded method or operator is guaranteed to be of the specified type, but any additional arguments need to be checked and handled by the class developer. In the previous example, r1 is always a ratnum, but r2 can be a ratnum, a scalar, or anything else (for example, a cell array).
Below is a list of all overloadable operators along with their corresponding method
names:
- +
- plus
- -
- minus
- *
- mtimes
- .*
- times
- /
- mrdivide
- ./
- rdivide
- \
- mldivide
- .\
- ldivide
- ==
- eq
- ~=
- ne
- ^
- mpower
- .^
- power
- uminus
- unary minus negates a single object instead of subtracting two objects
Properties can be protected from direct access using an access specifier. This
prevents code outside of the class from accessing or modifying these values. Only
class methods will be able to do
so.
classdef MyClass
properties (Access = private)
name
value
end
methods
function obj = MyClass(in_name, in_value)
obj.name = in_name;
obj.value = in_value;
end
end
end
m = MyClass(‘Ted’, 4)
m.name = ‘Bob’ % this triggers a run-time error