MATLAB: Instantiations of custom classes *sometimes* unique, sometimes not. Bug

handle classesMATLABoop

Hi!
First time poster.
I'm having trouble understanding how nested handle classes work. As far as I've understood handle objects are basically passed as reference, ie copies of an object copies the pointer to the original.
So I created this small sample and did not get what expected.
primaryclass.m
classdef primaryClass < handle
properties
x
myContainedClass = secondaryClass();
end
end
SecondaryClass.m
classdef secondaryClass < handle
properties
y
end
end
and a small script to present my problem:
a = primaryClass();
b = primaryClass();
a == b %returns false, as expected since handle objects are created separately.
a.myContainedClass == b.myContainedClass %Returns true, wait what?
As you can see the nested handle class are all pointers to the same object, which is not what I expected.
Im using a very large 3d matrix with custom handle objects that would benefit a lot from having nested unique handle object: inside every primaryClass object resides a unique secondaryClass object, unique to that particular primaryClass object instant.
What am I not understanding and missing?
Thanks for any help!
kind regards Robert Hedman
UPDATE
It seems that this fixes my problem, sometimes….
classdef primaryClass < handle
properties
x
myContainedClass
end
methods
function obj = primaryClass()
obj.myContainedClass = secondaryClass();
end
end
end
This fix makes sense since instantiating a variable within a constructor links the variable to that specific instance of that host object. Instantiating it outside apparently makes the variable a static variable for the entire class.
This does not appear to always work though, see below.
A(2,2,2) = primaryClass();
A(1,1,1).myContainedClass == A(2,2,2).myContainedClass %outputs false as expected
bigA(100,100,100) = primaryClass();
bigA(1,1,1).myContainedClass == bigA(2,2,2).myContainedClass %outputs true for some wierd reason...
Why is this? How do I get it to function as I expect?
kind regards Robert Hedman

Best Answer

I think you've already found the relevant documentation that explains what is happening. As to why it is like that, only Mathworks can tell you. In my opinion, there are many design flaws in the way they've implemented OOP (loadobj for example).
However, in your case all your problems come from the fact that you're trying to store arrays of handle objects. This is a bit unusual. Shouldn't your objects be value class instead? With handle classes, I tend to prevent concatenation / array construction by default because the class methods are rarely designed to work with array inputs.
If you do need handle classes then I would modify the class constructor so that it can create the array directly, avoiding the user having to remember to create each array element individually:
classdef primaryClass < handle
properties
x
myContainedClass
end
methods
function obj = primaryClass(varargin)
%call syntax:
% obj = primaryClass creates a scalar object
% obj = primaryClass(sz) creates an array of objects of size sz (sz a vector)
% obj = primaryClass(dim1, dim2, ...) creates an array of objects of size [dim1, dim2, ...]
if nargin == 0
obj.myContainedClass = secondaryClass;
else
sz = [varargin{:}]; %needs input checks
obj(prod(sz)) = primaryClass; %call the nargin == 0 version of the constructor
obj = reshape(obj, sz);
for idx = 1:numel(obj)-1
obj(idx).myContainedClass = secondaryClass;
end
end
end
end
end
Remember that each method of primaryClass has to be designed to work on arrays of objects, something that is very jarring if you're used to OOP in other languages:
a = primaryClass(5,2,2);
a.somemethod %the object received by somemethod will be a 5x2x2 primaryClass object.