MATLAB: Can I make MATLAB use a custom copy method when creating an array of the class

array of handle classhandle classesMATLABoop

I'm having an issue with a class that has properties that are handle classes. I've created this example to demonstrate the problem:
classdef Problem_Class < handle
properties (SetObservable = true)
Prop1
end
methods
function s = Problem_Class
s.Prop1 = Prop1_Class;
end
end
end
classdef Prop1_Class < handle
properties (SetObservable = true)
Prop2
end
methods
function s = Prop1_Class
s.Prop2 = Prop2_Class;
end
end
end
classdef Prop2_Class < handle
properties (SetObservable = true)
Name (1,:) char
end
methods
function s = Prop2_Class
s.Name = '';
end
end
end
So, If I create an instance of my Problem_Class:
problem = Problem_Class
The issue arises if I now create an array of Prop1, for example:
problem.Prop1.Prop2.Name = 'index 1';
problem.Prop1(8).Prop2.Name = 'index 8';
problem.Prop1(7).Prop2.Name = 'index 7';
If we look at the .Name property for each problem.Prop1.Prop2, I'd hope to see this:
[problem.Prop1.Prop2]; {ans.Name}'
{'index 1'}
{1×0 char }
{1×0 char }
{1×0 char }
{1×0 char }
{1×0 char }
{'index 7'}
{'index 8'}
But instead I get this:
{'index 1'}
{'index 7'}
{'index 7'}
{'index 7'}
{'index 7'}
{'index 7'}
{'index 7'}
{'index 8'}
The reason this is happening is explained here: https://www.mathworks.com/help/matlab/matlab_oop/initializing-arrays-of-handle-objects.html, where it is stated (at the end) "results in two calls to the class constructor. The first creates the object for array element A(4,5). The second creates a default object that MATLAB copies to all remaining empty array elements."
So, when I call problem.Prop1(8).Prop2.Name = 'index 8'; MATLAB calls the Prop1_Class constructor once, to populate problem.Prop1(8), and then a second time to create a Prop1_Class object that it copies to problem.Prop1(2:7). So now problem.Prop1(2:7) all point to the same object in memory. I don't want this! I need problem.Prop1(2:7) to be unique elements, and I need this to happen implicitly rather than having to pre-initialise the problem.Prop1() array.
I thought I had found the solution here: https://www.mathworks.com/help/matlab/matlab_oop/custom-copy-behavior.html, – I created a class based on the HandleCopy example on that page, and made Problem_Class, Prop1_Class, and Prop2_Class all inherit from that, so that all of them should have the custom copy behaviour. I have verified that, e.g. a = copy(b), where "b" is an instance of the Prop1_Class, then behaves as expected, creating an independent copy of b in a (such that a~=b, but their properties have the same values). However, MATLAB does not appear to call the custom copy method when creating an array!
Going back to the previous example: when I call problem.Prop1(8).Prop2.Name = 'index 8'; once MATLAB has created the problem.Prop1(8) element, and then creates the second Prop1_Class object and "copies" that to problem.Prop1(2:7), how do I get MATLAB to use a custom copy function, so I can prevent all problem.Prop1(2:7) pointing at the same object?
Many thanks for reading this post, all help gratefully received!

Best Answer

It seems like subsasgn() https://www.mathworks.com/help/matlab/ref/subsasgn.html could be helpful in this case. You can control the assignment behavior. The following code shows a very crude example. You can adapt and improve it according to your requirement
classdef Problem_Class < handle
properties (SetObservable = true)
Prop1
end
methods
function s = Problem_Class
s.Prop1 = Prop1_Class;
end
function A = subsasgn(A, S, B)
n = S(2).subs{:};
m = numel(A.Prop1);
num_new_elem = n-m;
if num_new_elem > 1
for i=1:num_new_elem-1
A.Prop1(m+i).Prop2.Name = '';
end
A.Prop1(m+num_new_elem).Prop2.Name = B;
else
A.Prop1(n).Prop2.Name = B;
end
end
end
end
Example
problem = Problem_Class;
problem.Prop1(1).Prop2.Name = 'index 1'; % the current definition of subsasgn requires to use index (1) for first element too
problem.Prop1(8).Prop2.Name = 'index 8';
problem.Prop1(5).Prop2.Name = 'index 7';
x = [problem.Prop1.Prop2];
{x.Name}'
Output
ans =
8×1 cell array
{'index 1'}
{1×0 char }
{1×0 char }
{1×0 char }
{'index 7'}
{1×0 char }
{1×0 char }
{'index 8'}