Documentation Center

  • Trial Software
  • Product Updates

Subclassing MATLAB Built-In Types

MATLAB Built-In Types

Built-in types represent fundamental kinds of data such as numeric arrays, logical arrays, and character arrays. Other built-in types contain data belonging to these fundamental types and other classes. For example, cell and struct arrays can contain instances of any class.

Built-in types define methods that perform operations on objects of these classes. For example, you can perform operations on numeric arrays, such as, sorting, rounding values, and element-wise and matrix multiplication. You can create an object of class double using an assignment statement, indexing expressions, or using converter functions.

See Fundamental MATLAB Classes for more information on MATLAB® built-in classes.

    Note:   It is an error to define a class that has the same name as a built-in class.

Why Subclass Built-In Types

Subclass a built-in type to extend the operations that you can perform on a particular class of data. For example, when you want to:

  • Define unique operations to perform on class data.

  • Be able to use methods of the built-in class and other built-in functions directly with objects of the subclass. For example, you do not need to reimplement all the mathematical operators if you derived from a class (for example, double) that defines these operators.

See Built-In Types You Cannot Subclass for a list of which MATLAB built-in classes you can subclass.

Which Functions Work With Subclasses of Built-In Types

Consider a class that defines enumerations. It can derive from an integer class and inherit methods that enable you to compare and sort values. For example, integer classes like int32 support all the relational methods (eq, ge, gt, le, lt, ne).

To see a list of functions that the subclass has inherited as methods, use the methods function:

methods('SubclassName')

Generally, you can use an object of the subclass with any of the inherited methods and any functions coded in MATLAB that normally accept input arguments of the same class as the superclass.

See Behavior of Built-In Functions with Subclass Objects for information on other required methods.

Built-In Types You Cannot Subclass

You cannot subclass the following built-in MATLAB classes:

  • char

  • cell

  • struct

  • function_handle

Behavior of Built-In Functions with Subclass Objects

When you define a subclass of a built-in class, the subclass inherits all built-in class methods. In addition, MATLAB provide a number of built-in functions as subclass methods. However, built-in functions that work on built-in classes behave differently with subclasses, depending on which function you are using and whether your subclass defines properties.

Behavior Categories

When you call an inherited method on a subclass of a built-in class, the result of that call depends on the nature of the operation performed by the method. The behaviors of these methods fit into several categories.

  • Operations on data values return objects of the superclass. For example, if you subclass double and perform addition on two subclass objects, MATLAB adds the numeric values and returns a value of class double.

  • Operations on the orientation or structure of the data return objects of the subclass. Methods that perform these kinds of operations include, reshape, permute, transpose, and so on.

  • Converting a subclass object to a built-in class returns an object of the specified class. Functions such as uint32, double, char, and so on, work with subclass objects the same as they work with built-in objects.

  • Comparing objects or testing for inclusion in a specific set returns logical or built-in objects, depending on the function. Functions such as isequal, ischar, isobject, and so on work with subclass objects the same as they work with superclass objects.

  • Indexing expressions return objects of the subclass. If the subclass defines properties, then default indexing no longer works and the subclass must define its own indexing methods. See Subclasses That Define Properties for more information.

  • Concatenation returns an object of the subclass. If the subclass defines properties, then default concatenation no longer works and the subclass must define its own concatenation methods. See Subclasses That Define Properties for more information.

To list the built-in functions that work with a subclass of a built-in class, use the methods function.

Subclasses That Define Properties

When a subclass of a built-in class defines properties, MATLAB no longer provides support for indexing and concatenation operations. MATLAB cannot use the built-in functions normally called for these operations because subclass properties can contain any data.

The subclass must define what indexing and concatenation mean for a class with properties. If your subclass needs indexing and concatenation functionality, then the subclass must implement the appropriate methods.

The sections that follow list the methods you must implement in the subclass to support indexing and concatenation. Also, the section Subclasses of Built-In Types with Properties provides an example of these methods.

Methods for Concatenation.  To support concatenation, the subclass must implement the following methods:

  • horzcat — Implement horizontal concatenation of objects

  • vertcat — Implement vertical concatenation of objects

  • cat — Implement concatenation of object arrays along specified dimension

Concatenation Functions

Methods for Indexing.  To support indexing operations, the subclass must implement these methods:

  • subsasgn — Implement dot notation and indexed assignments

  • subsref — Implement dot notation and indexed references

  • subsindex — Implement object as index value

Indexing Methods

More information on Built-In Methods

The following sections describe how different categories of methods behave with subclasses:

Extending the Operations of a Built-In Class

The MATLAB built-in class double defines a wide range of methods to perform arithmetic operations, indexing, matrix operation, and so on. Therefore, subclassing double enables you to add specific features without implementing many of the methods that a numeric class requires to function effectively in the MATLAB language.

The following class definition subclasses the built-in class double.

classdef DocSimpleDouble < double
   methods
      function obj = DocSimpleDouble(data)
         if nargin == 0
            data = 0;
         end
         obj = obj@double(data); % initialize the base class portion
      end
   end
end

You can create an instance of the class DocSimpleDouble and call any methods of the double class.

sc = DocSimpleDouble(1:10)
sc = 
   1x10 DocSimpleDouble:
  double data:
     1     2     3     4     5     6     7     8     9    10

Calling a method inherited from class double that operates on the data, like sum, returns a double and, therefore, uses the display method of class double:

sum(sc)
ans =
    55

You can index sc like an array of doubles. The returned value is the class of the subclass, not double:

a = sc(2:4)
a = 
  1x3 DocSimpleDouble:
  double data:
     2     3     4

Indexed assignment also works:

sc(1:5) = 5:-1:1
sc = 
   1x10 DocSimpleDouble:
  double data:
     5     4     3     2     1     6     7     8     9    10

Calling a method that modifies the order of the data elements operates on the data, but returns an object of the subclass:

sc = DocSimpleDouble(1:10);
sc(1:5) = 5:-1:1;
a = sort(sc)
a = 
   1x10 DocSimpleDouble:
  double data:
     1     2     3     4     5     6     7     8     9    10

Extending the Subclass.  You can extend the DocSimpleDouble with specialized methods to provide custom behavior. For example, see A Class to Manage uint8 Data.

Built-In Methods That Operate on Data Values

Most built-in functions used with built-in classes are actually methods of the built-in class. For example, the double and single classes both have a sin method. All of these built-in class methods work with subclasses of the built-in class.

When you call a built-in method on a subclass object, MATLAB uses the superclass part of the subclass object as inputs to the method, and the value returned is same class as the built-in class. For example:

sc = DocSimpleDouble(1:10);
a = sin(sc);
class(a)

ans =

double

Built-In Methods That Operate on Data Organization

This group of built-in methods reorders or reshapes the input argument array. These methods operate on the superclass part of the subclass object, but return an object of the same type as the subclass. Methods in this group include:

Indexing Methods

Built-in classes use specially implemented versions of the subsref, subsasgn, and subsindex methods to implement indexing (subscripted reference and assignment). When you index a subclass object, only the built-in data is referenced (not the properties defined by your subclass). For example, indexing element 2 in the DocSimpleDouble subclass object returns the second element in the vector:

sc = DocSimpleDouble(1:10);
a = sc(2)
a = 
  DocSimpleDouble
  double data:
     2

The value returned from an indexing operation is an object of the subclass. You cannot make subscripted references if your subclass defines properties unless your subclass overrides the default subsref method.

Assigning a new value to the second element in the DocSimpleDouble object operates only on the superclass data:

sc(2) = 12
sc = 
   1x10 DocSimpleDouble:
  double data:
     1    12     3     4     5     6     7     8     9    10

The subsref method also implements dot notation for methods. See Subclasses of Built-In Types with Properties for an example of a subsref method.

Concatenation Functions

Built-in classes use the functions horzcat, vertcat, and cat to implement concatenation. When you use these functions with subclass objects of the same type, MATLAB concatenates the superclass data to form a new object. For example, you can concatenate objects of the DocSimpleDouble class:

sc1 = DocSimpleDouble(1:10);
sc2 = DocSimpleDouble(11:20);
[sc1,sc2]
ans = 
  1x20 DocSimpleDouble:
  double data:
  Columns 1 through 13
     1     2     3     4     5     6     7     8     9    10    11    12    13
  Columns 14 through 20
    14    15    16    17    18    19    20
[sc1; sc2]
ans = 
  2x10 DocSimpleDouble:
  double data:
     1     2     3     4     5     6     7     8     9    10
    11    12    13    14    15    16    17    18    19    20

Concatenate two objects along a third dimension:

c = cat(3,sc1,sc2)
c = cat(3,sc1,sc2)
c = 
  DocSimpleDouble
  double data:
(:,:,1) =
     1     2     3     4     5     6     7     8     9    10
(:,:,2) =
    11    12    13    14    15    16    17    18    19    20

If the subclass of built-in class defines properties, you cannot concatenate objects of the subclass. Such an operation does not make sense because there is no way to know how to combine properties of different objects. However, your subclass can define custom horzcat and vertcat methods to support concatenation in whatever way makes sense for your subclass. See Concatenating DocExtendDouble Objects for an example.

A Class to Manage uint8 Data

This example shows a class derived from the built-in uint8 class. This class simplifies the process of maintaining a collection of intensity image data defined by uint8 values. The basic operations of the class include:

  • Capability to convert various classes of image data to uint8 to reduce object data storage.

  • A method to display the intensity images contained in the subclass objects.

  • Ability to use all the methods that you can use on uint8 data (for example, size, indexing (reference and assignment), reshape, bitshift, cat, fft, arithmetic operators, and so on).

The class data are matrices of intensity image data stored in the superclass part of the subclass object. This approach requires no properties.

The DocUint8 class stores the image data, which converts the data, if necessary:

classdef DocUint8 < uint8
   methods
      function obj = DocUint8(data)
      % Support no argument case
         if nargin == 0
            data = uint8(0);
         % If image data is not uint8, convert to uint8
         elseif ~strcmp('uint8',class(data))
            switch class(data)
               case 'uint16'
                  t = double(data)/65535;
                  data = uint8(round(t*255));
               case 'double'
                  data = uint8(round(data*255));
               otherwise
                  error('Not a supported image class')
            end
         end
         % assign data to superclass part of object
         obj = obj@uint8(data);
      end
      % Get uint8 data and setup call to imagesc
      function h = showImage(obj)
         data = uint8(obj);
         figure; colormap(gray(256))
         h = imagesc(data,[0 255]);
         axis image
         brighten(.2)
      end
   end
end

Using the DocUint8 Class

The DocUint8 class contains its own conversion code and provides a method to display all images stored as DocUint8 objects in a consistent way. For example:

cir = imread('circuit.tif');
img1 = DocUint8(cir);
img1.showImage;

Because DocUint8 subclasses uint8, you can use any of its methods. For example,

size(img1)
ans =
   280   272

returns the size of the image data.

Indexing Operations

Inherited methods perform indexing operations, but return objects of the same class as the subclass.

Therefore, you can index into the image data and call a subclass method:

showImage(img1(100:200,1:160));

Subscripted reference operations (controlled by the inherited subsref method) return a DocUint8 object.

You can assign values to indexed elements:

img1(100:120,140:160) = 255;
img1.showImage;

Subscripted assignment operations (controlled by the inherited subsasgn method) return a DocUint8 object.

Concatenation Operations

Concatenation operations work on DocUint8 objects because this class inherits the uint8 horzcat and vertcat methods, which return a DocUint8 object:

showImage([img1 img1]);

Data Operations

Methods that operate on data values, such as arithmetic operators, always return an object of the built-in type (not of the subclass type). For example, multiplying DocUint8 objects returns a uint8 object:

showImage(img1.*.8);
??? Undefined function or method 'showImage' for input arguments of type 'uint8'.

If you must be able to perform operations of this type, implement a subclass method to override the inherited method. The times method implements array (element-by-element) multiplication. See Implementing Operators for Your Class for a list of operator method names.

For example:

function o = times(obj,val)
   u8 = uint8(obj).*val; % Call uint8 times method
   o = DocUint8(u8);
end

Keep in mind that when you override a uint8 method, MATLAB calls the subclass method and no longer dispatches to the base class method. Therefore, explicitly call the uint8 times method or an infinite recursion can occur. Make the explicit call in this statement of the DocUint8 times method:

u8 = uint8(obj).*val;

After adding the times method to DocUint8, you can use the showImage method in expressions like:

showImage(img1.*1.8);

Subclasses of Built-In Types with Properties

When your subclass defines properties, indexing and concatenation do not work by default. There is really no way for the default subsref, horzcat, and vertcat methods to work with unknown property types and values. The following example subclasses the double class and defines a single property intended to contain a descriptive character string.

Methods Implemented

The following methods modify the behavior of the DocExtendDouble class:

  • DocExtendDouble — The constructor supports a no argument syntax that initializes properties to empty values.

  • subsref — Enables subscripted reference to the superclass part (double) of the subclass, dot notation reference to the DataString property, and dot notation reference the built-in data via the string Data (the double data property is hidden).

  • horzcat — Defines horizontal concatenation of DocExtendDouble objects as the concatenation of the superclass part using the double class horzcat method and forms a cell array of the string properties.

  • vertcat — The vertical concatenation equivalent of hortzcat (both are required).

  • char — A DocExtendDouble to char converter used by horzcat and vertcat.

  • dispDocExtendDouble implements a disp method to provide a custom display for the object.

Property Added

The DocExtendDouble class defines the DataString property to contain text that describes the data contained in instances of the DocExtendDouble class. Keep in mind that the superclass part (double) of the class contains the data.

Subclass with Properties

The DocExtendDouble class extends double and implements methods to support subscripted reference and concatenation.

classdef DocExtendDouble < double
   
   properties
      DataString
   end
   
   methods
      function obj = DocExtendDouble(data,str)
      % Support calling with zero arguments but do not return empty object
         if nargin == 0
            data = 0;
            str = '';
         elseif nargin == 1
            str = '';
         end
         obj = obj@double(data);
         obj.DataString = str;
      end
     
      function sref = subsref(obj,s)
         % Implements dot notation for DataString and Data
         % as well as indexed reference
         switch s(1).type
            case '.'
               switch s(1).subs
                  case 'DataString'
                     sref = obj.DataString;
                  case 'Data'
                     sref = double(obj);
                     if length(s)>1 && strcmp(s(2).type, '()')
                        sref = subsref(sref,s(2:end));
                     end
               end
            case '()'
               sf = double(obj);
               if ~isempty(s(1).subs)
                  sf = subsref(sf,s(1:end));
               else
                  error('Not a supported subscripted reference')
               end
               sref = DocExtendDouble(sf,obj.DataString);
         end
      end
     
      function newobj = horzcat(varargin)
         % Horizontal concatenation - cellfun calls double
         % on all object to get superclass part. cellfun call local char
         % to get DataString and the creates new object that combines
         % doubles in vector and chars in cell array and creates new object
         d1 = cellfun(@double,varargin,'UniformOutput',false );
         data = horzcat(d1{:});
         str = horzcat(cellfun(@char,varargin,'UniformOutput',false));
         newobj = DocExtendDouble(data,str);
      end
      
      function newobj = vertcat(varargin)
         % Need both horzcat and vertcat
        d1 = cellfun(@double,varargin,'UniformOutput',false );
        data = vertcat(d1{:});
        str = vertcat(cellfun(@char,varargin,'UniformOutput',false));
        newobj = DocExtendDouble(data,str);
      end

      function str = char(obj)
         % Used for cat functions to return DataString
         str = obj.DataString;
      end

      function disp(obj)
         % Change the default display
         disp(obj.DataString)
         disp(double(obj))
      end
   end
end

Create an instance of DocExtendDouble and notice that the display is different from the default:

ed = DocExtendDouble(1:10,'One to ten')
ed = 
One to ten
     1     2     3     4     5     6     7     8     9    10

The sum function continues to operate on the superclass part of the object:

sum(ed)
ans =
    55

Subscripted reference works because the class implements a subsref method:

ed(10:-1:1)

ans = 

One to ten
    10     9     8     7     6     5     4     3     2     1

However, subscripted assignment does not work because the class does not define a subsasgn method:

ed(1:5) = 5:-1:1
Error using DocExtendDouble/subsasgn
Cannot use '(' or '{' to index into an object of class 'DocExtendDouble' because
'DocExtendDouble' defines properties and subclasses 'double'.
Click here for more information.

The sort function works on the superclass part of the object:

sort(ed)
ans = 
One to ten
     1     2     3     4     5     6     7     8     9    10

Indexed Reference of a DocExtendDouble Object

Subscripted reference (performed by subsref) requires the subclass to implement its own subsref method.

ed = DocExtendDouble(1:10,'One to ten');
a = ed(2)
a = 
One to ten
     2
whos
  Name      Size            Bytes  Class              Attributes
  a         1x1               132  DocExtendDouble              
  ed        1x10              204  DocExtendDouble

You can access the property data:

c = ed.DataString
c =
One to ten
whos
  Name      Size            Bytes  Class         
  c         1x10               20  char           
  ed        1x10              204  DocExtendDouble 

Concatenating DocExtendDouble Objects

Given the following two objects:

ed1 = DocExtendDouble([1:10],'One to ten');
ed2 = DocExtendDouble([10:-1:1],'Ten to one');

You can concatenate these objects along the horizontal dimension:

hcat = [ed1,ed2]
hcat = 
    'One to ten'    'Ten to one'
  Columns 1 through 13
     1     2     3     4     5     6     7     8     9    10    10     9     8
  Columns 14 through 20
     7     6     5     4     3     2     1
whos
  Name      Size            Bytes  Class           
  ed1       1x10              204  DocExtendDouble 
  ed2       1x10              204  DocExtendDouble
  hcat      1x20              528  DocExtendDouble  

Vertical concatenation works in a similar way:

vcat = [ed1;ed2]
vcat = 
    'One to ten'    'Ten to one'
     1     2     3     4     5     6     7     8     9    10
    10     9     8     7     6     5     4     3     2     1

Both horzcat and vertcat return a new object of the same class as the subclass.

Understanding size and numel

The size function returns the dimensions of an array. The numel function returns the number of elements in an array.

The default size and numel functions behave consistently with user-defined classes (see Classes Not Derived from Built-In Classes). Other MATLAB functions use size and numel to perform their operations and you usually do not need to overload them.

When used with subclasses of built-in classes, the size and numel functions behave the same as in the superclasses.

Consider the built-in class double:

d = 1:10;
size(d)

ans =

     1    10

numel(d)

ans =

    10

dsubref = d(7:end);

whos dsub
  Name      Size            Bytes  Class     Attributes

  dsubref   1x4                32  double

The double class defines these behaviors, including parentheses indexing.

Subclass Inherited Behavior

Classes behave like their superclasses, unless the subclass explicitly overrides any given behavior. For example, DocSimpleDouble subclasses double, but defines no properties:

classdef DocSimpleDouble < double
   methods     
      function obj = DocSimpleDouble(data)
         if nargin == 0
            data = 0;
         end
         obj = obj@double(data);
      end
   end
end

Create an object and assign to the superclass part of the object the values 1:10:

sd = DocSimpleDouble(1:10);

The size function returns the size of the superclass part:

size(sd)

ans =

     1    10

The numel function returns the number of elements in the superclass part:

numel(sd)

ans =

     10

Object arrays return the size of the built-in arrays also:

size([sd;sd])

ans =

     2    10

numel([sd;sd])

ans =

     20

The DocSimpleDouble class inherits the indexing behavior of the double class:

sdsubref = sd(7:end);
whos sdsubref
  Name       Size            Bytes  Class              Attributes

  sdsubref   1x4                88  DocSimpleDouble  

Classes Not Derived from Built-In Classes

Consider a simple value class. It does not inherit the array-like behaviors of the double class. For example:

classdef VerySimpleClass
   properties
      Value
   end
end

Create an instance of this class and assign a ten-element vector to the Value property:

vs = VerySimpleClass;
vs.Value = 1:10;
size(vs)

ans =

     1     1

numel(vs)

ans =

     1

 size([vs;vs])

ans =

     2     1

numel([vs;vs])

ans =

     2

vs is a scalar object, as opposed to an array of VerySimpleClass objects. The Value property is an array of doubles:

size(vs.Value)

ans =

     1    10

Apply indexing expressions to the object property:

vssubref = vs.Value(7:end);
whos vssubref
  Name       Size            Bytes  Class     Attributes

  vssubref   1x4                32  double   

vs.Value is an array of class double:

class(vs.Value)

ans =

double

Creating an array of VerySimpleClass objects

vsArray(1:10) = VerySimpleClass;

MATLAB does not apply scalar expansion to object array property value assignment. Use the deal function for this purpose:

[vsArray.Value] = deal(1:10);

Indexing rules for object arrays are equivalent to those of struct arrays:

v1 = vsArray(1).Value;
>> whos v1
  Name      Size            Bytes  Class     Attributes

  v1        1x10               80  double

vsArray(1).Value(6)

ans =

     6

Changing the Behavior of size

Subclasses of built-in numeric classes inherit a size method, which operates on the superclass part of the subclass object (this method is hidden). If you want size to behave in another way, you can override it by defining your own size method in your subclass.

Keep in mind that other MATLAB functions use the values returned by size. If you change the way size behaves, ensure that the values returned make sense for the intended use of your class.

Avoid Overloading numel

It is important to understand the significance of numel with respect to indexing. MATLAB calls numel to determine the number of elements returned by an indexed expression like:

A(index1,index2,...,indexn)

Both subsref and subsasgn use numel:

  • subsrefnumel computes the number of expected outputs (nargout) returned subsref

  • subsasgnnumel computes the number of expected inputs (nargin) that MATLAB assigns as a result of a call to subsasgn

Subclasses of built-in classes always return scalar objects as a result of subscripted reference and always use scalar objects for subscripted assignment. The numel function returns the correct value for these operations and there is, therefore, no reason to overload numel.

If you define a class in which nargout for subsref or nargin for subsasgn is different from the value returned by the default numel, then overload numel for that class to ensure that it returns the correct values.

A Class to Represent Hardware

This example shows the implementation of a class to represent an optical multiplex card. These cards typically have a number of input ports, which this class represents by the port data rates and names. There is also an output port. The output rate of a multiplex card is the sum of the input port data rates.

The DocMuxCard class defines the output rate as a Dependent property, and then defines a get access method for this property. The get.OutPutRate method calculates the actual output rate whenever the OutPutRate property is queried. See Property Get Methods for more information on this technique.

Why Derive from int32

The DocMuxCard class derives from the int32 class because 32–bit integers represent the input port data rates. The DocMuxCard class inherits the methods of the int32 class, which simplifies the implementation of this subclass.

Class Definition

Here is the definition of the DocMuxCard class. Notice that the input port rates initialize the int32 portion of class.

classdef DocMuxCard < int32
   properties
      InPutNames % cell array of strings
      OutPutName % a string
   end
   properties (Dependent = true)
      OutPutRate
   end
   methods
      function obj = DocMuxCard(inptnames, inptrates, outpname)
         obj = obj@int32(inptrates); % initial the int32 class portion
         obj.InPutNames = inptnames;
         obj.OutPutName = outpname;
      end
      function x = get.OutPutRate(obj)
         x = sum(obj); % calculate the value of the property
      end
      function x = subsref(card, s)
           if strcmp(s(1).type,'.')
              base = subsref@int32(card, s(1));
              if isscalar(s)
                 x = base;
              else
                 x = subsref(base, s(2:end));
              end
           else
              x = subsref(int32(card), s);
           end
        end
   end
end

Using the Class with Methods of int32

The constructor takes three arguments:

  • inptnames — Cell array of input port names

  • inptrates — Vector of input port rates

  • outpname — Name for the output port

>> omx = DocMuxCard({'inp1','inp2','inp3','inp4'},[3 12 12 48],'outp')
omx = 
  1x4 DocMuxCard array with properties:
    InPutNames: {'inp1'  'inp2'  'inp3'  'inp4'}
    OutPutName: 'outp'
    OutPutRate: 75
  int32 data:
           3          12          12          48

You can treat an DocMuxCard object like an int32. For example, this statement accesses the int32 data in the object to determine the names of the input ports that have a rate of 12:

>> omx.InPutNames(omx==12)
ans = 
    'inp2'    'inp3'

Indexing the DocMuxCard object accesses the int32 vector of input port rates:

>> omx(1:2)
ans =
           3          12

The OutPutRate property get access method uses sum to sum the output port rates:

>> omx.OutPutRate
ans =
    75
Was this topic helpful?