is it possible to call a static method from another static method, both method belonging to the same class? Or is there some special syntax that I am not aware of? Currently I am trying to do the following:
% Circle.m
classdef Circle
methods (Static)
function p = getPi
p = 3.14;
end
function a = getArea( r )
a = r * Circle.getPi * Circle.getPi;
end
end
end
And then:
>> Circle.getPi
ans =
3.1400
>> Circle.getArea(123)
Undefined variable "Circle" or class "Circle.getPi".
Error in utils.Circle.getArea (line 8)
a = r * Circle.getPi * Circle.getPi;
See the section Referencing Package Members Within Packages in the documentation page called "Packages Create Namespaces". Basically it says that normal methods from instances of a class do not require the package prefix, but with static methods it is required. Evidently this applies even when calling a static method, from another class method!
All references to packages, functions, and classes in the package must use the package name prefix, unless you import the package. (See Importing Classes.) For example, call a package function with this syntax:
z = mypack.pkfcn(x,y);
Note that definitions do not use the package prefix. For example, the function definition line of the pkfcn.m function would include only the function name:
[snip]
Calling class methods does not require the package name because you have an instance of the class:
obj.myMethod(arg) or
myMethod(obj,arg)
A static method requires the full class name:
mypack.myClass.stMethod(arg)
The error message shows that your class is utils.Circle, not Circle. Your class is placed inside a package utils.
Related
I have following class definition under +mypackage\MyClass.m
classdef MyClass
properties
num
end
methods (Static)
function obj = with_five()
obj = MyClass(5);
end
end
methods
function obj = MyClass(num)
obj.num = num;
end
end
end
I use with_five() as a static factory method.
Following script should create two objects.
import mypackage.MyClass
class_test1 = MyClass(5);
class_test2 = MyClass.with_five();
class_test1 has been created.
For class_test2 it says:
Error using MyClass.with_five
Method 'with_five' is not defined for class 'MyClass' or is removed from MATLAB's search path.
Error in Testpackage (line 4)
class_test2 = MyClass.with_five();
When I put MyClass.m outside of a package folder and remove the "import" statement, it works.
What am I doing wrong?
There are several wonky things in MATLAB with packages and with static methods.
First, functions within a package must use the package name to reference other functions or classes within the same package (see here). So the static method mypackage.MyClass.with_five() must be declared as follows:
methods (Static)
function obj = with_five()
obj = mypackage.MyClass(5);
end
end
Without this, I see:
>> mypackage.MyClass.with_five
Undefined function or variable 'MyClass'.
Error in mypackage.MyClass.with_five (line 8)
obj = MyClass(5);
Second, static class methods (at least the ones for classes inside packages) are not loaded until an object of the class is created. So we need to call the class constructor first:
>> clear classes
>> mypackage.MyClass.with_five
Undefined variable "mypackage" or class "mypackage.MyClass.with_five".
>> mypackage.MyClass(1);
>> mypackage.MyClass.with_five
ans =
MyClass with properties:
num: 5
The same is true if we import the class:
>> clear classes
>> import mypackage.MyClass
>> MyClass.with_five
Undefined variable "mypackage" or class "mypackage.MyClass.with_five".
>> MyClass(3);
>> MyClass.with_five
ans =
MyClass with properties:
num: 5
This second point was true in R2017a (where the outputs above were generated), but is no longer true in R2021a. I don't know in which release of MATLAB this was fixed, but in R2021a it is no longer necessary to create an object of a class to use its static method.
I think what you are missing is the fact that when you import a static method, you have to import the class name and the method name (https://au.mathworks.com/help/matlab/matlab_oop/importing-classes.html)
This works:
classdef MyClass
properties
num
end
methods (Static)
function obj = with_five()
obj = MyClass(5);
end
end
methods
function obj = MyClass(num)
obj.num = num;
end
end
end
Then do the following:
>> import MyClass.with_five
>> x = with_five
Output:
x =
MyClass with properties:
num: 5
That being said: don't create an object in a member function of its own class! As you suggest, a better choice is to move the factory to a different class. If you wanted to make a bunch of chainsaws, you would never go about trying to design a chainsaw that has a button on it that builds chainsaws. You would rather design a factory that can produce chainsaws that are designed to cut down trees.
A 'static factory' is not a bad thing. This is actually a pretty common pattern especially in C#. However, the factory is always its own class. Calling that method from another object that inherits from or extends that class (which I'm not sure you can even do in Matlab...) would most certainly be confusing if not deadly. I also can't think of any reason you would ever need to do this. In fact I don't understand why with_five() should be static at all
In MATLAB, one of the attributes of a class (defined after classdef) is Sealed, which means that no class can use it as a superclass (or to be more precise, "to indicate that these classes have not been designed to support subclasses."1).
For example, if I try to instantiate a class that's defined as below (considering table is Sealed):
classdef SomeLie < table
end
I would get the 'MATLAB:class:sealed' error:
>> A = SomeLie;
Error using SomeLie
Class 'table' is Sealed and may not be used as a superclass.
As I refuse to be told by a machine what I may or may not do, I would like to subclass a Sealed class, regardless. How can I do that in MATLAB R2017a?
I'm having a hard time believing that this system is completely airtight, so I'm looking for a solution that would cause the Sealed attribute to be silently ignored (or something of that sort). The desired solution should work without modifying any "library class definitions" to remove Sealed from them.
I tried playing around with "reflection", but arrived at a dead end...
classdef SomeLie % < table
properties (Access = private)
innerTable table;
end
properties (GetAccess = public)
methodHandles struct = struct();
end
methods
function slObj = SomeLie(varargin)
slObj.innerTable = table(varargin{:});
% methodHandles = methods(slObj.innerTable);
ml = ?table; ml = {ml.MethodList.Name}.';
ml = setdiff(ml,'end');
tmpStruct = struct;
for indM = 1:numel(ml)
tmpStruct.(ml{indM}) = str2func([...
'#(varargin)' ml{indM} '(slObj.innerTable,varargin{:})']);
end
slObj.methodHandles = tmpStruct;
end
function varargout = subsref(slObj,varargin)
S = struct(slObj);
varargout{:} = S.methodHandles.(varargin{1}.subs)(varargin{:});
end
end
end
(There's no need to fix the above code, I was just sharing)
I do not think the machine is the problem, but the class designer and he certainly has good motivations to seal the class. "Philosophy" of coding, a part, you could 'own' the class in a wrapper class without defining it sealed.
For example, supposer the class Hello is sealed and has a method (or function, if you wish) sayHello which you would like to use in inherited classes you could define a class FreeHello (public) which contains an instance of Hello. At the constructor you build the corresponding Hello and then you define a sayHello method whose body simply calls your Hello instance and makes it execute the sayHello method (and returns the output, accordingly).
In order to 'open' the sealed class, you need to do these for all properties and public methods; of course you are still not capable of accessing private methods, but now you can subclass your wrapper class, as you wish.
How can I avoid repeating a long tedious package name in matlab classes in the following cases:
When specifying the Superclass, e.g. classdef Class < tediouspkgname.Superclass
When calling the superclass constructor, e.g. obj = obj#tediouspkgname.Superclass(...).
When calling superclass methods, e.g. val = somefunc#tediouspkgname.Superclas(...).
I'm looking for an equivalent of matlabs import statement, which is not usable in these cases unfortunately.
MWE:
Lets have a folder called +tediouspkgname/ in our Matlab path. So Matlab recognizes there's a package called tediouspkgname.
Lets have a Class ExampleClass which is saved in the file +tediouspkgname/ExampleClass.m:
classdef ExampleClass
properties
p
end
methods
function obj = ExampleClass(p)
obj.p = p;
end
function print(obj)
fprintf('p=%s\n',obj.p);
end
end
end
Let there be another Class, derived from ExampleClass, living in the file
+tediouspkgname/DerivedClass.m:
classdef DerivedClass < tediouspkgname.ExampleClass
methods
function obj = DerivedClass(p)
obj = obj#tediouspkgname.ExampleClass(p);
end
function print(obj)
print#tediouspkgname.ExampleClass(obj);
fprintf('--Derived.\n');
end
end
end
I want the following commands to work without errors while mentioning tediouspkgname. as little as possible:
e = tediouspkgname.ExampleClass('Hello');
e.print();
o = tediouspkgname.DerivedClass('World');
o.print();
In particular, this definition of DerivedClass gives me the error ExampleClass is not a valid base class:
classdef DerivedClass < tediouspkgname.ExampleClass
methods
function obj = DerivedClass(p)
obj = obj#tediouspkgname.ExampleClass(p);
end
function print(obj)
import tediouspkgname.ExampleClass
print#ExampleClass(obj);
fprintf('--Derived.\n');
end
end
end
You have two examples at the command line:
e = tediouspkgname.ExampleClass('Hello');
e.print();
o = tediouspkgname.DerivedClass('World');
o.print();
For these cases, you can use import at the command line:
import tediouspkgname.*
e = ExampleClass('Hello');
e.print();
o = DerivedClass('World');
o.print();
and it should work fine.
For the other cases you have (in the class definition line, and when calling a superclass method), you need to use the fully qualified name including the package.
I dislike this aspect of the MATLAB OO system. It's not just that it's tedious to write out the fully qualified name; it means that if you change the name of your package, or move a class from one package to another, you have to manually go through your whole codebase in order to find-and-replace one package name for another.
I would like to access the class name of the concrete class that's invoking a static method implemented in an abstract superclass.
This is the code (part of) of the abstract superclasss:
classdef (Abstract) AbstractJobProcessor < handle
properties (Abstract, Constant)
VERSION_MAJOR;
VERSION_MINOR;
LAST_MODIFIED;
end
...
methods (Static)
function res = getVersionMajor;
res = AbstractJobProcessor.VERSION_MAJOR;
end
function res = getVersionMinor
res = AbstractJobProcessor.VERSION_MINOR;
end
function res = getVersionInfo
res = sprintf('**CLASSNAME**: v%d.%02d (last modified: %s)',...
AbstractJobProcessor.VERSION_MAJOR,...
AbstractJobProcessor.VERSION_MINOR,...
AbstractJobProcessor.LAST_MODIFIED);
end
end
...
Basically, I would like to access the classname of the concrete subclass and use it in the method getVersionInfo in place of the string **CLASSNAME**.
All the methods returning meta information about a class (that I have found in the documentation) require a reference to an instance of the class (like, for example, mc = metaclass(object)).
The below function will give you what you want - subclass name, that was used when invoking an (inherited) static superclass method. Just call it inside your superclass method like you would any normal function:
className = getStaticCallingClassName();
What it does handle:
Both the case when method was invoked programmatically (i.e. by a running script / function), as well as when it was invoked from the command window.
Arbitrarily nested package names (i.e. classes located inside directories prefixed with +).
What it does not handle:
Does not work if the static method is called in a non-static context, i.e. on an object instance. But you should not be using such syntax anyway. This would've been possible if we were able to use evalin with 'caller' workspace recursively, but it does not work this way.
A brief explanation behind the idea: second entry in the stack trace, produced by dbstack, would correspond to the superclass, which we can use to extract the static method name. The next steps depend on:
If the method is invoked programmatically, third stack entry would point us to a line in the the parent script/function which we need to read, e.g. using dbtype. All that's left to do is extract the subclass name using regexp based on the method name.
If the method is invoked from command window, we query the last command and use that as the input for our regular expression.
Note that even if stack has 3 entries or more, it doesn't mean that the method was invoked programmatically. For example, if we've stopped on a breakpoint somewhere and invoke the method from command window, stack trace would be long, but regexp based on the line from the third stack trace entry will not give us the answer. In this case we fall back to the command window approach.
Warning: it heavily relies on undocumented features and may break in any feature release. Tested on Matlab 2015b, but should work on most previous releases as well. Some may say it is quite dirty, but it works very well, and it's the only method that I'm aware of to achieve such a behavior.
function [className, fullPath] = getStaticCallingClassName()
ST = dbstack('-completenames');
% First one is getStaticCallingClassName, second one is the superclass
methodName = char(regexp(ST(2).name, '[^\.]([^.]*)$', 'match'));
% matches string (combination of alphanumeric/underscore/dot characters) preceeding the given method call.
pattern = sprintf('[\\w.-]*(?=.%s)', methodName);
% If the parent called static method programmatically, we should be able to find it via the next (third) stack trace
if length(ST) > 2
command = evalc('dbtype(ST(3).file, num2str(ST(3).line))');
className = char(regexp(command, pattern, 'match'));
else % was likely called from command window. Long stack trace means that we're simply waiting in a breakpoint somewhere
className = []; % go straight to command window approach
end
if isempty(className) % means that static method was called directly from command window
javaHistory = com.mathworks.mlservices.MLCommandHistoryServices.getSessionHistory();
command = char(javaHistory(end));
className = char(regexp(command, pattern, 'match'));
end
fullPath = which(className);
end
Here's a workaround. According to the MATLAB documentation:
'Ordinary methods define functions that operate on objects of the class',
'Static methods are (1) associated with a class, but (2) not with specific instances of that class'.
You can have both aspects of static methods if you call an ordinary method with an empty object array.
For example, suppose we have a base class:
classdef base
methods
function obj = base()
disp('constructor called')
end
function dispClassName(obj)
disp(['class name = ', class(obj)]);
end
end
end
and a subclass
classdef sub < base
end
Now call the methods as follows (this will not invoke any constructor):
>> base.empty.dispClassName
class name = base
>> sub.empty.dispClassName
class name = sub
A real solution (for which I did an enhancement request 03315500 to MathWorks) would be to extend the MATLAB language with a method attribute 'Class' to define methods that are associated with the invoking class (similar to the Python #classmethod decorator). Methods of this class would automatically receive the metaclass of the invoking function as a first argument. With such an extension we could define a base class:
% Future MATLAB syntax extension
classdef base
methods(Class) % New method attribute ‘Class’
function dispClassName(cls) % implicit argument (meta.class)
disp(['class name = ' cls.Name ]);
end
end
end
and a subclass
classdef sub < base
end
and call
>> base.dispClassName
class name = base
>> sub.dispClassName
class name = sub
I have a class and a function, I want to put function in class a just wanted to call the whole in another class, but it gives the certain error while calling, Is it a possibility to call function without calling it in Class constructor? I'm currently calling in class constructor but other possible way is more likely. Five arguments are required in func, how can I make that function a class?
I have tried also in constructor while input argument is giving obj.arg1=arg1;
My Code:
classdef myClass
properties
node;
end
properties (Access=private)
end
methods
function obj = myClass()
func(obj,obj,obj,obj,obj);
end
function node = func(arg1,arg2,arg3,arg4,arg5)
%some operation
end
end
You want to have a separate methods(Static) section for those functions you want to call without instantiating an instance of your class. For any methods in the static section, you can do from another file:
<some code here>
answer = myClass.myStaticMethod(args);
<rest of code here>
Whereas for anything in the generic methods block without (Static) you would have to instantiate the class and then call methods against the instance, i.e.:
<some code here>
classInstance = myClass(constructor args)
answer = classInstance.myNonStaticMethod(args);
<rest of code here>