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
Related
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
I'd like to save a particular class property to disk, while ignoring the rest of the class properties. I think MATLAB allows the saveobj method to be overridden for this purpose. However, this saves the class object with only that property attached. I want to save the property itself, without any of the class information.
I might think that a suitable method would look like:
classdef myClass
properties
myProp
end
methods
def b = saveobj(a)
b = a.myProp;
end
def Save(a,fname)
save(fname,'a.myProp');
end
end
end
But neither of these have the desired effect. Can anyone help me, please?
You can overload the save function itself without having to go through saveobj:
classdef myClass
properties
myProp
end
methods
function [] = save(a,fname,varargin)
myProp = a.myProp; %#ok<PROP,NASGU>
save(fname,'myProp',varargin{:});
end
end
end
Then on the command window:
>> foo = myClass();
>> foo.myProp = 4;
>> foo.save('var.txt');
>> bar = load('var.txt','-mat');
>> bar.myProp
ans =
4
The first method (the one involving saveobj) is actually correct. For the purposes of discussion let us consider this simple class:
classdef testclass
properties
x
end
methods
function this = testclass(x)
this.x = x ;
end
function a = saveobj(this)
a = this.x ;
end
end
end
When you ask MATLAB to save an instance of your class, it will use the saveobj method when you call save if one exists. The output of this method can be an object, a struct, an array, whatever. You want to test that this has occurred, and you do something natural like this:
>> obj = testclass('hi')
obj =
testclass with properties:
x: 'hi'
>> save tmp.mat obj
>> clear all
>> load tmp.mat
>> obj
obj =
testclass with properties:
x: []
>>
And this is where I suspect your confusion arises. You expect obj to be a char but instead it's an empty object of class testclass. (You can verify that it is just an instance of the object based on the saved definition of the class, and that it is not created by calling the empty constructor.)
This may seem rather confusing until you understand how loadobj works. In order for MATLAB to know which static method to call on load, it saves the class definition in conjunction with whatever output you are providing from your custom saveobj method. When you call load it then loads the class definition and calls the loadobj static method if one exists. We can test this by modifying the class definition above:
classdef testclass
properties
x
end
methods
function this = testclass(x)
this.x = x ;
end
function a = saveobj(this)
a = this.x ;
end
end
methods( Static )
function this = loadobj(a)
this = testclass(a) ;
end
end
end
If you set a breakpoint in the loadobj method you can verify that the type of a is indeed char as you expect.
I generally find the set/get interface defined in hgsetget very useful for setting and getting multiple parameters at the same time. I recently found it was especially suited for object construction.
Example:
classdef testclass < hgsetget
properties
A
B
C
D
end
methods
function obj = testclass(varargin)
if ~isempty(varargin)
set(obj,varargin{:})
end
end
end
end
Usage:
>> a = testclass('A',1,'B',2)
a =
testclass handle
Properties:
A: 1
B: 2
C: []
D: []
Despite the slowness of this interface I'm really happy of the flexibility it provides.
What is more annoying for my application is that I obtain a handle class (by inheritance from hgsetget).
To circumvent this my first guess was to construct an abstract class with my set/get definition inside. Very simply written it gives:
classdef (Abstract) myAbstractClass
methods
function obj = set(obj,varargin)
for i = 1:2:length(varargin)
obj.(varargin{i}) = varargin{i+1};
end
end
function val = get(obj,varargin)
val = cell(length(varargin),1);
for i = 1:length(varargin)
val{i} = obj.(varargin{i});
end
end
end
end
and then set it as superclass for my test class
classdef testclass < myAbstractClass
properties
A
B
C
D
end
methods
function obj = testclass(varargin)
if ~isempty(varargin)
set(obj,varargin{:})
end
end
end
end
However I must misunderstand something in the construction mechanism because here is what happens:
>> a = testclass('A',1,'B',2)
ans =
testclass
Properties:
A: 1
B: 2
C: []
D: []
Methods, Superclasses
a =
testclass
Properties:
A: []
B: []
C: []
D: []
Methods, Superclasses
If somebody knows the reason of this behavior, I'm totally open to her/his explanations
Thank you in advance
JM
Overall your design looks good, but within your constructor method testclass, you need to write
obj = set(obj,varargin{:});
Because you are using a value class rather than a handle class, you need to retrieve the output of your set method, otherwise you'll have set the property of something, and then discarded it and returned a completely different (and empty) obj as output.
Also, you need to add a semi-colon after the call to set; otherwise it will display the intermediate something.
You may also want to revise your opinion of handle classes; they're really very natural to use (just my opinion, but more natural in an OO context than value-behaviour).
I'm working on classes and their multiple inheritance, I have a problem which I couldn't solve after so many help, I have a class A which is base class and class B, which is its derive class. What I want is, the Constructor of class A with input arguments is to be calling in derived class B,supposed to be called in class B with its input arguments But unfortunately i have error Not enough input arguments. as Class B is expecting arguments but I want to give arguments as input in Class B like above.
What solution is to be suggested or appropriate in my case?
My Code: (Base Class A)
classdef A %base class
properties
arg1
end
properties
out
end
methods
function obj = A(arg1)
obj.arg1=arg1;
obj.out=[1 2 3;];
end
end
end
Derived Class B:
classdef B < A %derived Class
properties (Access=protected)
arg2
obj1
end
methods
function obj1 = B(arg2)
obj1.arg2=arg2;
A(obj1);
end
end
end
Call the constructor of superclass A like this:
B.m:
classdef B < A %derived Class
properties (Access=protected)
arg2
end
methods
function obj = B(arg1,arg2)
obj = obj#A(arg1);
obj.arg2 = arg2;
end
end
end
From the documentation:
By default, MATLAB calls the superclass constructor without arguments. If you want the superclass constructor called with specific arguments, explicitly call the superclass constructor from the subclass constructor. The call to the superclass constructor must come before any other references to the object.
Don't forget to clear all instances of both classes before trying new code, and clear A.m B.m just for good measure.
Usage:
>> myA = A(1)
myA =
A with properties:
arg1: 1
out: [1 2 3]
>> myB = B(1,2)
myB =
B with properties:
arg1: 1
out: [1 2 3]
I read this documentation page about how to invoke superclass constructor from a child class. The syntax they mention is this:
obj = obj#MySuperClass(SuperClassArguments);
I'm wondering what the purpose of # symbol in the above syntax is. Is the # symbol just a meaningless place occupier in the syntax or does the # symbol represent the function handle symbol in MATLAB?
If I use:
obj = MySuperClass(SuperClassArguments);
instead of
obj = obj#MySuperClass(SuperClassArguments);
it still works fine. So what is the purpose of using # symbol?
1) no this has nothing to do with function handles, this is the syntax used to call the superclass constructor
2) you could try it and see for yourself. Here is an example:
A.m
classdef A < handle
properties
a = 1
end
methods
function obj = A()
disp('A ctor')
end
end
end
B.m
classdef B < A
properties
b = 2
end
methods
function obj = B()
obj = obj#A(); %# correct way
%#obj = A(); %# wrong way
disp('B ctor')
end
end
end
With the correct syntax, we get:
>> b = B()
A ctor
B ctor
b =
B with properties:
b: 2
a: 1
If you use the commented line instead of the first one, you get the following error:
>> clear classes
>> b = B()
A ctor
A ctor
B ctor
When constructing an instance of class 'B', the constructor must preserve the class of the returned
object.
Error in B (line 8)
disp('B ctor')