Weird MATLAB behavior when having containers.Map as a class property - matlab

I create my own class as below:
classdef testClass < handle
properties
value;
map = containers.Map('KeyType','double','ValueType','any');
end
end
My goal is for each object of testClass to maintain its own map. However, it turns out that there is only one map object that for the whole class: all objects of testClass access to the same containers.Map. For example, if I create two objects as follows
a = testClass;
b = testClass;
a.value = 'a';
b.value = 'b';
a.map(1) = 123;
b.map(2) = 321;
It ends up both a and b's map contains two key-value pairs:
>> a
a =
testClass handle
Properties:
value: 'a'
map: [2x1 containers.Map]
>> b
b =
testClass handle
Properties:
value: 'b'
map: [2x1 containers.Map]
Methods, Events, Superclasses
Both (key,value) pairs (1,123) and (2,321) appears in both a.map and b.map
>> a.map.keys
ans =
[1] [2]
>> a.map.values
ans =
[123] [321]
>> b.map.keys
ans =
[1] [2]
>> b.map.values
ans =
[123] [321]
Is this a bug? How can I keep independent containers.Map for each class object?

The problem is not that testClass is a handle, but rather that the initial value specified in the properties block is not evaluated when you think it is. MATLAB calculates the default value of class properties only once when the class is loaded, and then gives that value to each new instance of your class.
You can see this by looking at the metaclass for your testClass. For example:
c = testClass;
c.map(1) = 42;
hc = ?testClass;
hc.PropertyList(2).DefaultValue.keys % returns [1]
hc.PropertyList(2).DefaultValue.values % returns [42]
If you want each instance to have a different map, you must construct the map explicitly in the constructor. (And yes, I've been there, done that).

Related

Property initialization in MATLAB

In MATLAB there are two ways to initialize its properties. Either directly in the properties:
classdef A
properties
foo = 'bar';
end
end
or by explicitly defining a constructor:
classdef B
properties
foo;
end
methods this = B()
this.foo = 'bar';
end
end
Which one is the more preferable way?
(I am asking because there is a similar case in C++ where the preferred way is to initialize the member variables with a colon after the constructor rather than assigning variables within the constructor).
In most cases, the two methods behave the same, and the choice is just a preference depending on which you find clearer/easier to maintain etc.
But they are not the same, and there are cases where you will get yourself in trouble if you aren't aware of the differences.
The essential difference is that properties that are initialised in the constructor are initialised separately each time an object is constructed, whereas properties that are initialised in the properties block with a default value are initialised once, when the class definition is first read. The default value in a properties block is the default value of the class, not of the object; and (using reflection) you can query that default value even if no instance of the class has yet been instantiated.
In most cases, this makes no difference - but when the initial value is a handle object, or the output of a non-determinate function it does. So consider the following two classes:
classdef A
properties
foo
end
methods
function obj = A
obj.foo = containers.Map;
end
end
end
classdef B
properties
foo = containers.Map
end
end
Note that containers.Map is a handle class.
In class A, every time you create a new instance of A you get a new/different containers.Map for its foo. In B, every instance of B gets the same containers.Map for its foo, as the property is initialised only once, the first time the class definition is read. So if you modify foo for any object of class B, that change is propagated to all other instances of class B, as you can see:
>> a1 = A; a2 = A; a1.foo('greeting') = 'hello'; a2.foo('greeting') = 'bonjour';
>> a1.foo('greeting'), a2.foo('greeting')
ans =
'hello'
ans =
'bonjour'
>> b1 = B; b2 = B; b1.foo('greeting') = 'hello'; b2.foo('greeting') = 'bonjour';
>> b1.foo('greeting'), b2.foo('greeting')
ans =
'bonjour'
ans =
'bonjour'
>> % Note that b1.foo has changed as a result of setting b2.foo
This point about handle classes as default values often trips people up; but the behaviour is not specific to handle classes. For example, consider the following variation:
classdef A
properties
foo
end
methods
function obj = A
obj.foo = datetime('now');
end
end
end
classdef B
properties
foo = datetime('now')
end
end
Here A will store the creation time of each object, whereas B will store the time at which the class was first initialised, for all objects, no matter when they were created.
In case you find this behaviour confusing, see https://undocumentedmatlab.com/blog/handle-object-as-default-class-property-value, and particularly the comment thread underneath that article, for a discussion of the issue, and an explanation for the reasons MATLAB is designed in this way.
Edit: Great follow up question in the comments, regarding the behaviour of clear and its relation to this issue.
Using the second implementation of the classes above (with datetime), look at the following:
>> a = A; b = B; datestr(a.foo), datestr(b.foo)
ans =
'01-Sep-2018 18:59:30'
ans =
'01-Sep-2018 18:59:30'
>> clear variables
>> a = A; b = B; datestr(a.foo), datestr(b.foo)
ans =
'01-Sep-2018 18:59:48'
ans =
'01-Sep-2018 18:59:30'
>> clear classes
>> a = A; b = B; datestr(a.foo), datestr(b.foo)
ans =
'01-Sep-2018 18:59:57'
ans =
'01-Sep-2018 18:59:57'
So we first of all create an A and a B, and display their foos, and they both show the same time. Then we wait a little while, do clear variables, and we do it again. Note that the foo from A is the new time, and the foo from B is still the same as before. Finally we wait a little more time, we do clear classes, and we do it again. This time both A and B have the new time.
Why? Because clear variables merely removes references to the variables from the workspace. The class definition of B is not cleared, so when we create another B, it still uses the value from when the class definition was first read. clear classes, by contrast, also removes the class definition, so when we later construct a new B, it gets that time, as the class definition is then reread. All this is irrelevant to A, as foo is just given a value at construction time.
Note that clear classes clears all class definitions: you can clear the definition of only class B using clear B.
It is on your choice except for some cases.
Let me mention when we use the colon in Constructor function inC++:
1- Calling base class constructors
2- Initializing member variables before the body of the constructor executes.
No.#1 can be also used in the body but we should use the colon in No.#2 when the member is const.
BUT
But here is Matlab, with its own language syntax and structure.
Use which one do your job.
But here in Matlab when you want to define Constant properties you MUST define it in properties part and you can't do it in initializer/constructor function.
classdef NamedConst
properties (Constant)
R = pi/180; % Can't define it in constructor body, because it is `Constant properties`
end
%class body
end

Using 'optional' properties in a MATLAB class

I've a question concerning classes in MATLAB.
I'm writing a parser, that doesn't always have the same input. Some variables are not defined at all times. Here is a short mock up script:
test_parser.m
classdef test_parser < matlab.mixin.Copyable
properties (AbortSet = true)
a
b
end
end
make_class.m
function result = make_class(array)
result = test_parser;
result.a = array(1);
if length(array)>1
result.b=array(2);
end
end
Now calling from the command window with different input lengths:
>> make_class([10])
ans =
test_parser with properties:
a: 10
b: []
>> make_class([10,20])
ans =
test_parser with properties:
a: 10
b: 20
In both cases the variable b is a property of test_parser, as specified. My wish would be, that b is optional, so just present if there is b in the input.
What is the best way to achieve this? I guess an optional parameter is not really a property?
If you want optional properties, you can inherit your class from dynamicprops. You can then add properties on the fly using the command addprop, test whether a property exists using isprop, and even respond to properties being added or removed by listening to the events PropertyAdded and PropertyRemoved.
In your example, you would then use:
classdef test_parser < matlab.mixin.Copyable & dynamicprops
properties (AbortSet = true)
a
end
end
function result = make_class(array)
result = test_parser;
result.a = array(1);
if length(array)>1
result.addprop('b')
result.b=array(2);
end
end
>> make_class([10])
ans =
test_parser with properties:
a: 10
>> make_class([10,20])
ans =
test_parser with properties:
a: 10
b: 20

Side effects of calling a MATLAB class instance don't persist

If I make the following toy class in MATLAB:
classdef testIt
properties
a
b
c
end
methods
function obj = testIt
obj.a = 1;
obj.b = 2;
end
function obj = set.a(obj,a)
obj.a = a;
end
function obj = set.b(obj,b)
obj.b = b;
end
function obj = addup(obj)
obj.c = obj.a + obj.b;
end
end
end
and then instantiate and call the addup method:
>> aTest = testIt
Properties:
a: 1
b: 2
c: []
>> aTest.addup
Properties:
a: 1
b: 2
c: 3
>> aTest
Properties:
a: 1
b: 2
c: []
The property c has not been created. Instead, I need to use the syntax:
>> aTest = aTest.addup
>> aTest
Properties:
a: 1
b: 2
c: 3
Can anyone explain to me why this is necessary?
Matlab supports two types of classes: handle classes, and value classes.
Value classes operate similar to structures and other Matlab variables, in that they are passed by value. Thus, if you want to modify an instance of a value class within a function, you need to return and overwrite the instance in the calling workspace.
Handle classes, on the other hand, are passed by reference. If you change the value of a property anywhere, the class is updated in all workspaces. For example, you can have a reference to the object in the base workspace, and one inside a GUI, and if you modify one, the other changes its value accordingly.
If you change your code to classdef testIt < handle, the objects will behave exactly the way you expect.
Also: Have a look at the documentation
add to the class definition:
classdef testIt < handle

Override how the value of a user defined class is displayed when it is a field of a structure

I have created my own class to act like an enumerated type. I haver overridden the method disp() so when a variable containing that type is displayed in the Command Window it shows something meaningful (specifically, the name of that enumerated value.)
classdef MyEnumeratedType
properties(Constant)
ENUMVAL1 = MyEnumeratedType(1, 'ENUMVAl1');
ENUMVAL2 = MyEnumeratedType(2, 'ENUMVAL2');
ENUMVAL3 = MyEnumeratedType(3, 'ENUMVAL3');
end
properties(Access=private)
ordinal
name
end
methods(Access=private)
function this = MyEnumeratedType(ord, name)
this.ordinal = ord;
this.name = name;
end
end
methods
function disp( this )
disp(this.name);
end
end
end
So when I assign it to a variable in the command window, I get the desired output:
>> x = MyEnumeratedType.ENUMVAL2
x =
ENUMVAL2
So far so good. BUT when I assign a value of type MyEnumeratedType to the field of a structure, the display of that structure doesn't display the value, but only tells me that I have a value of type MyEnumeratedType.
>> mystruct.field1 = 42
mystruct =
field1: 42
>> mystruct.field2 = MyEnumeratedType.ENUMVAL3
mystruct =
field1: 42
field2: [1x1 MyEnumeratedType]
How do I get the value of field2 to show up like it does for the double value in field1?
The disp method for structures shows the contents of numeric and cell arrays, if they can be written in a row, and the class/size info otherwise:
s = struct('a',1,'b',[1 2 3],'c',{{1}},'d',magic(3),'e',[1;2])
s =
a: 1
b: [1 2 3]
c: {[1]}
d: [3x3 double]
e: [2x1 double]
Consequently, to have the value of your enum displayed, you need to overload disp for structures. To do this, you create an #struct directory on your path, and create your own disp method that hopefully reproduces faithfully what Matlab does, but with an exception for your particular class. In short: it's possible, but I'd rather not be the one doing that.
This submission http://www.mathworks.com/matlabcentral/fileexchange/48637 attempts to recreate the disp function pretty well. So, you could use this for the disp.m you put in the #struct folder.

Setting an object property using a method in Matlab

I am creating a class in MATLAB and while I have little experience with objects, I am almost positive I should be able to set a class property using a class method. Is this possible in MATLAB?
classdef foo
properties
changeMe
end
methods
function go()
(THIS OBJECT).changeMe = 1;
end
end
end
f = foo;
f.go;
t.changeMe;
ans = 1
Yes, this is possible. Note that if you create a value object, the method has to return the object in order to change a property (since value objects are passed by value). If you create a handle object (classdef foo<handle), the object is passed by reference.
classdef foo
properties
changeMe = 0;
end
methods
function self = go(self)
self.changeMe = 1;
end
end
end
As mentioned above, the call of a setting method on a value object returns the changed object. If you want to change an object, you have to copy the output back to the object.
f = foo;
f.changeMe
ans =
0
f = f.go;
f.changeMe
ans =
1