| MATLAB® | ![]() |
| On this page… |
|---|
Behavior of Built-In Functions with Subclass Objects Example — A Class to Manage uint8 Data |
Built-in classes represent fundamental kinds of data such as numeric arrays, logical arrays, and character arrays. Other classes tend to combine data belonging to these fundamental classes. cell arrays and struct arrays are other fundamental classes.
Built-in classes define methods that enable you to perform many operations on objects of these classes. For example, the MATLAB language enables you to create objects of class double using an assignment statement, to create arrays of doubles, and to perform complex operations on these arrays such as matrix multiplication, indexing, and so on.
See Classes (Data Types) for more information on these classes.
Note It is an error to define a class that has the same name as a built-in class. |
You can derive classes from most MATLAB built-in classes (see Built-In Classes You Cannot Subclass). Subclassing a built-in class is useful when you want to extend the operations that you can perform on a particular class of data.
Typically, you create a class that primarily uses data of a built-in class and you want to be able to use the methods of that built-in class directly with objects of your class. For example, if you subclass the built-in class double, you can use an object of your subclass anywhere a double is expected. See Behavior of Built-In Functions with Subclass Objects for information on methods your subclass might require.
Consider a class that defines named constants. It might derive from integral classes and thereby 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).
You cannot subclass the following built-in MATLAB classes:
char
cell
struct
function_handle
To list the methods of a built-in class, use the methods command. For example:
methods double
When you create a class that derives from a MATLAB built-in class, built-in methods and functions become available to the subclass as methods. When you call one of these built-in methods, it generally acts on the built-in part of the subclass. For example, if you subclass double, and then perform addition on two subclass objects, MATLAB adds the parts of the objects that are doubles and the object returned is of class double.
Built-in functions that work on built-in classes behave in different ways, depending on which function you are using and if your subclass defines properties.
Functions that operate on objects of the superclass effectively become subclass methods.
Subclasses that do not define properties inherit the superclass's methods. Adding properties to a subclass of a built-in class prevents some superclass methods from being able to work with your subclass. If your class needs the functionality provided by these methods, you must implement your own versions:
subsref — Implement dot notation and indexing
horzcat — Implement horizontal concatenation of objects
vertcat — Implement vertical concatenation of objects
The following sections describe how different categories of methods behave with subclasses:
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 needs to function usefully in the MATLAB language.
The following class definition subclasses the built-in class double.
classdef DocSimpleDouble < double methods function obj = DocSimpleDouble(data) 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 =
DocSimpleDouble
double data:
1 2 3 4 5 6 7 8 9 10
Methods, Superclasses
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 =
55You can index sc like an array of doubles. Note that the returned value is the class of the subclass, not double:
>> a = sc(2:4)
a =
DocSimpleDouble
double data:
2 3 4Indexed assignment also works:
>> sc(1:5) = 5:-1:1
sc =
DocSimpleDouble
double data:
5 4 3 2 1 6 7 8 9 10
Methods, SuperclassesCalling 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 =
DocSimpleDouble
double data:
1 2 3 4 5 6 7 8 9 10
Methods, SuperclassesMost 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, only the built-in (i.e., superclass) parts of the subclass object are used as inputs to the method, and the value returned is same class as the built-in class, not your subclass.
This group of built-in methods reorders or reshapes the input argument array. These methods operate on the built-in (i.e., superclass) part of the subclass object, but return an object of the same type as the subclass. Methods in this group include:
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
Methods, Superclasses
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 built-in data:
>> sc(2) = 12
sc =
DocSimpleDouble
double data:
1 12 3 4 5 6 7 8 9 10
Methods, SuperclassesHowever, the data property of the built-in part of the DocSimpleDouble object is hidden:
>> sc.data
??? Error using ==> subsref
No appropriate method, property, or field data for class DocSimpleDouble.The subsref method also implements dot notation for methods. See Example — Adding Properties to a Built-In Subclass for an example of a subsref method.
Built-in classes use the functions horzcat, vertcat, and cat to implement concatenation. When you use these function with subclass objects of the same type, the built-in data parts are concatenated 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 =
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
Methods, Superclasses
>> [sc1; sc2]
ans =
DocSimpleDouble
double data:
1 2 3 4 5 6 7 8 9 10
11 12 13 14 15 16 17 18 19 20
Methods, SuperclassesConcatenate 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
Methods, SuperclassesNote that you cannot concatenate subclass objects of built-in classes if the subclass defines properties. Such an operation does not make sense if there are properties defined because there is no way to know how to combine properties of different objects. However, your subclass can define custom horzcat and vertcat methods support concatenation in whatever way makes sense for your subclass. See Concatenating DocExtendDouble Objects for an example.
This example shows how deriving a class from the built-in uint8 class can simplify 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 (e.g., size, indexing (reference and assignment), reshape, bitshift, cat, fft, arithmetic operators, and so on).
The class data are matrices of intensity image data, which are stored in the built-in part of the subclass so no properties are required.
The DocUint8 class stores the image data in the built-in (uint8) part of the object, which is initialized in the constructor after the data has been converted, if necessary:
classdef DocUint8 < uint8 methods function obj = DocUint8(data) % Support no argument case if nargin == 0 data = uint8([]); % 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 built-in 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
The DocUint8 class contains its own conversion code and provide a method to display all images stored as DocUint8 objects in a consistent way. You can only call subclass (DocUint8) methods using dot notation, because the uint8 class does not define dot notation of its methods. For example:
>> cir = imread('circuit.tif');
>> img1 = DocUint8(cir);
>> img1.showImage;

Because DocUint8 is derived from uint8, you can use any of its methods. For example,
>> size(img1) ans = 280 272
returns the size of the image data.
Inherited methods perform indexing operations, but return objects of the same class as the subclass.
You can index into the image data:
>> 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;
Subscripted assignment operations (controlled by the inherited subsasgn method) return a DocUint8 object.

Concatenation operations work on DocUint8 objects because this class inherits the uint8 horzcat and vertcat methods, which return a DocUint8 object:
>> showImage([img1 img1]);

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 need to be able to perform operations of this type, you must 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 software calls the subclass method and no longer dispatches to the base class method. Therefore, you must explicitly call the uint8 times method or an infinite recursion can occur. The explicit call is made 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);

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.
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 built-in 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 built-in part using the double horzcat 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.
disp — DocExtendDouble implements a disp method to provide a custom display for the object.
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 built-in part (double) of the class contains the data.
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) if nargin == 0 data = []; 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 built-in 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 10The sum function continues to operate on the built-in part of the object:
>> sum(ed)
ans =
55Subscripted assignment works on the built-in part of the object:
>> ed(1:5) = 5:-1:1
ed =
One to ten
5 4 3 2 1 6 7 8 9 10The sort function works on the built-in part of the object:
>> sort(ed)
ans =
One to ten
1 2 3 4 5 6 7 8 9 10While subscripted assignment (performed by subsasgn) operates on the built-in part of the DocExtendDouble object by default, 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
a 1x1 84 DocExtendDouble
ed 1x10 156 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 156 DocExtendDouble
You can access the built-in part of the object using dot notation with Data because this capability is provided by the DocExtendDouble subsref method:
>> d = ed.Data
d =
1 2 3 4 5 6 7 8 9 10
>> whos
Name Size Bytes Class
d 1x10 80 double
ed 1x10 156 DocExtendDouble
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 156 DocExtendDouble
ed2 1x10 156 DocExtendDouble
hcat 1x20 376 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 1Both horzcat and vertcat return a new object of the same class as the subclass.
Both size and numel work by default with subclasses of built-in classes, whether or not the subclasses define properties. However, the returned values are based on different parts of the subclass object. For example, consider subclass objects, one that defines no properties and one that does define a property:
>> sd = DocSimpleDouble(1:10); >> ed = DocExtendDouble(1:10,'One to ten');
The size function returns the size of the built-in part of the objects:
>> size(sd)
ans =
1 10
>> size(ed)
ans =
1 10The numel function treats objects differently:
>> numel(sd)
ans =
1
>> numel(ed)
ans =
1In each case, numel counts the number of objects, not the size of the built-in array:
>> size([ed;ed])
ans =
2 10
>> numel([ed;ed])
ans =
1Subclasses of built-in classes inherit a size method, which as stated above, operates on the built-in part of the subclass object. 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.
It is important to understand the significance of numel with respect to indexing. Both subsref and subsasgn use numel:
subsref — numel computes the number of expected outputs (nargout) returned subsref
subsasgn — numel computes the number of expected inputs (nargin) to be assigned from 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 you must overload numel for that class to ensure it returns the correct values.
This example shows the implementation of a class to represent an optical multiplex card. These cards typically have a number of input ports, which are represented here by their data rates and their 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. This means the actual output rate is calculated when it is required. See Property Get Methods for more information on this technique.
The DocMuxCard class is derived from the int32 class because the input port data rates are being represented by 32–bit integers. The DocMuxCard class inherits the methods of the int32 class, which simplifies the implementation of this subclass.
Here is the definition of the DocMuxCard class. Notice that the input port rates are used to 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
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 = OMuxCard({'inp1','inp2','inp3','inp4'},[3 12 12 48],'outp')
omx =
DocMuxCard
Properties:
InPutNames: {'inp1' 'inp2' 'inp3' 'inp4'}
OutPutName: 'outp'
OutPutRate: 75
int32 data:
3 12 12 48
Methods, SuperclassesYou can treat an OMuxCard 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(find(omx==12))
ans =
'inp2' 'inp3'Indexing the OMuxCard object accesses the int32 vector of input port rates:
>> omx(1:2)
ans =
3 12The OupPutRate property get access method makes use of sum to sum the output port rates:
>> omx.OutPutRate
ans =
75
![]() | Subclassing from Multiple Classes | Abstract Classes and Interfaces | ![]() |
| © 1984-2008- The MathWorks, Inc. - Site Help - Patents - Trademarks - Privacy Policy - Preventing Piracy - RSS |