Class with Modified Indexing

How to Modify Class Indexing

This example defines a class that modifies the default indexing behavior by implementing subsref and subsasgn methods. The class also implements type conversion and addition by implementing a double converter method and a plus method.

The objective of the class design is to:

  • Enable you to treat an object of the class as a numeric array

  • Be able to contain nonnumeric and numeric data in an object of the class

Class Description

The class has three properties:

  • Data — numeric test data

  • Description — description of test data

  • Date — date test was conducted

Assume that you have the following random data (randi):

d = randi(9,3,4)
d =

     8     9     3     9
     9     6     5     2
     2     1     9     9

Create an instance of the class:

obj = MyDataClass(d,'Test001')
obj = 

  MyDataClass with properties:

           Data: [3x4 double]
    Description: 'Test001'
           Date: [2012 1 7 9 32 34.5190]

The constructor arguments pass the values for the Data and Description properties. The clock function assigns the value to the Date property from within the constructor. This approach captures the time and date information when each instance is created.

Here is the preliminary code listing without the subsref, subsasgn double, and plus methods.

classdef MyDataClass
   properties
      Data
      Description
   end
   properties (SetAccess = private)
      Date
   end
   methods
      function obj = MyDataClass(data,desc)
         if nargin > 0
            obj.Data = data;
         end
         if nargin > 1
            obj.Description = desc;
         end
         obj.Date = clock;
      end
   end
end

Specialize Subscripted Reference — subsref

Implement a subsref method to support both the default and a specialized type of indexing.

  • The default indexed reference behavior for scalar objects:

    obj.Data(2,3)
    ans =
    
         5
  • And to add the functionality to index into the Data property with an expression like this statement:

    obj(2,3)
    

If you redefine '()' indexing to support access to the Data property, you cannot create arrays of MyDataClass objects and use '()' indexing to access individual objects. You can reference only scalar objects.

To achieve the design goals, the subsref method must handle all indexing types. The subsref method:

  • Calls the builtin subsref function for '.' indexing

  • Returns an error for '{}' indexing

  • Defines its own version of '()' indexing.

The result: obj(i) is equivalent to obj.Data(i).

function sref = subsref(obj,s)
   % obj(i) is equivalent to obj.Data(i)
   switch s(1).type
      case '.'
         sref = builtin('subsref',obj,s);
      case '()'
         if length(s) < 2
            sref = builtin('subsref',obj.Data,s);
            return
         else
            sref = builtin('subsref',obj,s);
         end
      case '{}'
         error('MYDataClass:subsref',...
            'Not a supported subscripted reference')
   end
end

Specialize Subscripted Assignment — subsasgn

To support the equivalent of the indexed reference behavior with indexed assignment, implement a subsasgn method.

  • Support the default indexed assignment:

    obj.Data(2,3) = 9;
  • Add the functionality to assign values to the Data property with an expression like this statement:

    obj(2,3) = 9;

Like the subsref method, the subsasgn method:

  • Calls the builtin subsasgn function for '.' indexing

  • Returns an error for '{}' indexing

  • Defines its own version of '()' indexing.

The substruct function redefines the index type and index subscripts structure that MATLAB® passes to subsref and subsasgn.

function obj = subsasgn(obj,s,val)
   if isempty(s) && isa(val,'MyDataClass')
      obj = MyDataClass(val.Data,val.Description);
   end
   switch s(1).type
      case '.'
         obj = builtin('subsasgn',obj,s,val);
      case '()'
         if length(s)<2
            if isa(val,'MyDataClass')
               error('MyDataClass:subsasgn',...
                  'Object must be scalar')
            elseif isa(val,'double')
               % Redefine the struct s to make the call: obj.Data(i)
               snew = substruct('.','Data','()',s(1).subs(:));
               obj = subsasgn(obj,snew,val);
            end
         end
      case '{}'
         error('MyDataClass:subsasgn',...
            'Not a supported subscripted assignment')
   end
end

Implement Addition for Object Data — double and plus

First, implement a double method that converts an object to an array of doubles. By implementing a double converter method, it is possible to add a MyDataClass object to another class of object. However, the other class must implement a double method that also returns an array of doubles. For more information on type conversion, see Object Converters.

Allow direct addition of the Data property values by implementing a plus method. Implementing a plus method enables the use of the + operator for addition of MyDataClass objects.

Because the plus method implements addition by adding double arrays, MATLAB:

  • Apply the rules of addition when adding MyDataClass objects

  • Returns errors for any condition that can cause errors in default numeric addition. For example, dimension mismatch.

The plus method uses the double method to convert the object to numeric values before performing the addition:

function a = double(obj)
   a = obj.Data;
end
function c = plus(obj,b)
   c = double(obj) + double(b);
end

For example, the plus method enables you to add a scalar number to the object Data array.

Here are the values of the Data, displayed using indexed reference:

obj(:,:)
ans =

     8     9     3     9
     9     6     9     2
     2     1     9     9

Add 7 to the array contained in the Data property:

obj + 7
ans =

    15    16    10    16
    16    13    16     9
     9     8    16    16

MyDataClass.m

This definition for MyDataClass includes the end indexing method discussed in end as Object Index.

classdef MyDataClass
   % Example for "A Class with Modified Indexing"
   properties
      Data
      Description
   end
   properties (SetAccess = private)
      Date
   end
   methods
      function obj = MyDataClass(data,desc)
         % Support 0-2 args
         if nargin > 0
            obj.Data = data;
         end
         if nargin > 1
            obj.Description = desc;
         end
         obj.Date = clock;
      end
      
      function sref = subsref(obj,s)
         % obj(i) is equivalent to obj.Data(i)
         switch s(1).type
            case '.'
               sref = builtin('subsref',obj,s);
            case '()'
               if length(s)<2
                  sref = builtin('subsref',obj.Data,s);
                  return
               else
                  sref = builtin('subsref',obj,s);
               end
            case '{}'
               error('MyDataClass:subsref',...
                  'Not a supported subscripted reference')
         end
      end
      
      function obj = subsasgn(obj,s,val)
         if isempty(s) && isa(val,'MyDataClass')
            obj = MyDataClass(val.Data,val.Description);
         end
         switch s(1).type
            case '.'
               obj = builtin('subsasgn',obj,s,val);
            case '()'
               %
               if length(s)<2
                  if isa(val,'MyDataClass')
                     error('MyDataClass:subsasgn',...
                        'Object must be scalar')
                  elseif isa(val,'double')
                     snew = substruct('.','Data','()',s(1).subs(:));
                     obj = subsasgn(obj,snew,val);
                  end
               end
            case '{}'
               error('MyDataClass:subsasgn',...
                  'Not a supported subscripted assignment')
         end
      end
      
      function a = double(obj)
         a = obj.Data;
      end
      
      function c = plus(obj,b)
         c = double(obj) + double(b);
      end
      
      function ind = end(obj,k,n)
         szd = size(obj.Data);
         if k < n
            ind = szd(k);
         else
            ind = prod(szd(k:end));
         end
      end
   end
end

Related Topics