cancel
Showing results for 
Show  only  | Search instead for 
Did you mean: 
Try the Materials Informatics Toolkit, which is designed to easily handle SMILES data. This and other helpful add-ins are available in the JMP® Marketplace
Choose Language Hide Translation Bar
peri_a
Level III

Class inheritance, what am i missing?

I run into a problem while writing a series of classes inheriting from each other.

i stripped the code to the basic example below for simplicity.

Basically i have a series of classes inheriting from each other in cascade (called MyBaseClass, MyDerivedClass, MyDoubleDerived)and they differ for the value assigned to the F_Type property (value 1 2 and 3 respectively). i am creating an instance of MyDoubleDerived Class assigning it to a variable (and also storing into an Associative array where i can get to it by its ID).

I see that during instantiation the F_Type is correclty set to 3 as well as when i try to reach the property from the object itself.

However when i am inside a method the value of  this:F_Type  or F_Type is for some reason 2 (but if i am accessing from the associative array storing the object the value is correctly 3). Weird enough the method is defined on the Base class (that has the F_Type= 1 so i cannot understand why the value is 2. could this be a bug of some type? (i see this both in JMP 17.1.0 and JMP 18 Early release, I do not have earlier version available to test)

 

 

If(!Namespace Exists( "MyNameSp" ),
	NS=New Namespace( "MyNameSp" );
);

MyNameSp:ARList=associative array(); // i store here the object that i can access via its key for convenience

// below the fucntion to instantiate the object from the class MyNameSp:CreateNewObj=Function({ClassName, NewKey}, {Default Local}, ObjRef=NewObject(ClassName,{NewKey}); show(ObjRef:ObjID); MyNameSp:ARList[ObjRef:ObjID]=ObjRef; return(ObjRef); ); Define Class( "MyBaseClass", F_Type=1; ObjID=""; _init_=Method({NewKey}, this:ObjID=NewKey; show("value during initialization"); show(this:F_Type); show(F_Type); ); MyMethod=Method({}, show("value in method"); show(F_Type); show(this:F_Type); show(this:ObjID); show(MyNameSp:ARList[this:ObjID]:F_Type); ); ); Define Class( "MyDerivedClass", baseclass( MyBaseClass ), F_Type=2; ); //////////////////////////////////////////////////////////////////////////// Define Class( "MyDoubleDerived", baseclass( MyDerivedClass ), F_Type=3; );
// start of the actual script SampleObj=MyNameSp:CreateNewObj("MyDoubleDerived","Key1"); show("value From script"); show(SampleObj:F_Type); SampleObj:MyMethod();

 

 

7 REPLIES 7
Craige_Hales
Super User

Re: Class inheritance, what am i missing?

Start with How to use Define Class 

I have not used classes in a while, but I'm pretty certain that you don't want to respecify the F_Type variable as part of the subclass definition; you should be setting it in the subclass _init_ method.

Craige
Craige_Hales
Super User

Re: Class inheritance, what am i missing?

This is probably closer to what you want

If(!Namespace Exists( "MyNameSp" ),
	NS=New Namespace( "MyNameSp" );
);

MyNameSp:ARList=associative array(); // i store here the object that i can access via its key for convenience
// below the fucntion to instantiate the object from the class
MyNameSp:CreateNewObj=Function({ClassName, NewKey},
{Default Local},
	
	ObjRef=NewObject(ClassName,{NewKey});
	show(ObjRef:ObjID);
	MyNameSp:ARList[ObjRef:ObjID]=ObjRef;
	return(ObjRef);
);


Define Class( "MyBaseClass",
	
	F_Type=1;
	ObjID="";
	
	_init_=Method({NewKey},
		this:ObjID=NewKey;
		show("value during initialization");
		show(this:F_Type);
		show(F_Type);
	);


	MyMethod=Method({},
		show("value in method");
		show(F_Type);
		show(this:F_Type);
		show(this:ObjID);
		show(MyNameSp:ARList[this:ObjID]:F_Type);
	);
);

Define Class( "MyDerivedClass",
	baseclass( MyBaseClass ),
//	F_Type=2;
	_init_=Method({NewKey},
		super:_init_(NewKey);
		F_Type=2;
		show("MyDerivedClass",F_Type);
	);
);

////////////////////////////////////////////////////////////////////////////
Define Class( "MyDoubleDerived",
	baseclass( MyDerivedClass ),
//	F_Type=3;	
	_init_=Method({NewKey},
		super:_init_(NewKey);
		F_Type=3;
		show("MyDoubleDerived",F_Type);
	);

);
// start of the actual script
SampleObj=MyNameSp:CreateNewObj("MyDoubleDerived","Key1");
show("value From script");
show(SampleObj:F_Type);


SampleObj:MyMethod();
"value during initialization";
this:F_Type = 1;
F_Type = 1;
// above, the base class is constructed (at beginning of intermediate)
"MyDerivedClass";
F_Type = 2;
// above, the intermediate class is constructed (at the beginning of most derived)
"MyDoubleDerived";
F_Type = 3;
// above, the most derived class is constructed
ObjRef:ObjID = "Key1";
"value From script";
// below: there is really only one variable F_Type, belonging to the base class
SampleObj:F_Type = 3;
"value in method";
F_Type = 3;
this:F_Type = 3;
this:ObjID = "Key1";
MyNameSp:ARList[this:ObjID]:F_Type = 3;

the derived classes use super:_init_ to make sure their base class is fully constructed, then continue on to initialize the derived class. Redefining the F_Type at each layer is hiding the base class F_Type, not replacing it. I don't understand why your example shows F_Type=2 instead of F_Type=1 inside the method call, but the problem goes away with the usage pattern above.

 

 

Craige
peri_a
Level III

Re: Class inheritance, what am i missing?

Thanks for the reply.

While readapting the code is easy enough that i can continue working i am not really understanding what is behind the curtains.

when i am calling the properties directly it gets the right value (3), it is only from within the method that the value is 2 (and not 1) i also tried to add another level (and is always the n-1 level that is considered).

 

Additionally this raises another question what about inheritance of methods that are also stored in variables... should i also define the specific methods from within the"_init_"?.

 

 

peri_a
Level III

Re: Class inheritance, what am i missing?

I double checked and the same behavior is true also for methods.

if you call a specific method from another a method and the "target" method differs in the different level of inheritance, the method of the "one level up" class in the stack is used (not sure the wording makes sense).

similarly if i define the method of the derived class from within "_init_" method all works as expected

While for variable i can understand the logic of doing in the  "_init_" method, for the methods seems counter intuitive.

Is there any documentation on it?

 

here the modified example:

If(!Namespace Exists( "MyNameSp" ),
	NS=New Namespace( "MyNameSp" );
);

MyNameSp:ARList=associative array();

MyNameSp:CreateNewObj=Function({ClassName, NewKey},
{Default Local},
	
	ObjRef=NewObject(ClassName,{NewKey});
	show(ObjRef:ObjID);
	MyNameSp:ARList[ObjRef:ObjID]=ObjRef;
	return(ObjRef);
);


Define Class( "MyBaseClass",
	
	F_Type=1;
	K_Type=1;
	ObjID="";
	
	_init_=Method({NewKey},
		this:ObjID=NewKey;
		
		
	);
	
	MyChangeInitMethod=Method({PassedType, passedID},
		show("INIT Base Level method (1) ",PassedType, passedID);
	);
	
	MyMethod=Method({},
		this:MyChangeMethod(this:F_Type,this:ObjID);
		this:MyChangeInitMethod(this:F_Type,this:ObjID);
	);
	
	MyChangeMethod=Method({PassedType, passedID},
		show("Base Level method (1) ",PassedType, passedID);
	);
);

Define Class( "MyDerivedClass",
	baseclass( MyBaseClass ),
	F_Type=2;
	
	_init_=Method({NewKey},
		this:ObjID=NewKey;
		K_Type=2;
		MyChangeInitMethod=Method({PassedType, passedID},
			show("INIT Derived class Method (2)",PassedType, passedID);
		);
	);
	
	MyChangeMethod=Method({PassedType, passedID},
		show("Derived class Method (2)",PassedType, passedID);
	);
);

////////////////////////////////////////////////////////////////////////////
Define Class( "MyDoubleDerived",
	baseclass( MyDerivedClass ),
	F_Type=3;
	
	_init_=Method({NewKey},
		this:ObjID=NewKey;
		K_Type=3;
		MyChangeInitMethod=Method({PassedType, passedID},
			show("INIT double Derived class method (3)",PassedType, passedID);
		);
	);
	MyChangeMethod=Method({PassedType, passedID},
		show("double Derived class method (3)",PassedType, passedID);
	);
);

////////////////////////////////////////////////////////////////////////////
Define Class( "MyTripleDerived",
	baseclass( MyDoubleDerived ),
	F_Type=4;
	
	_init_=Method({NewKey},
		this:ObjID=NewKey;
		K_Type=4;
		MyChangeInitMethod=Method({PassedType, passedID},
			show("INIT Triple Derived class method (4)",PassedType, passedID);
		);
	);
	
	MyChangeMethod=Method({PassedType, passedID},
		show("Triple Derived class method (4)",PassedType, passedID);
	);
);

SampleObj1=MyNameSp:CreateNewObj("MyBaseClass","Key1");
SampleObj2=MyNameSp:CreateNewObj("MyDerivedClass","Key2");
SampleObj3=MyNameSp:CreateNewObj("MyDoubleDerived","Key3");
SampleObj4=MyNameSp:CreateNewObj("MyTripleDerived","Key4");
show("----------------Calling the method via a method-------------------");
show("       1       ");
SampleObj1:MyMethod();
show("       2       ");
SampleObj2:MyMethod();
show("       3       ");
SampleObj3:MyMethod();
show("       4       ");
SampleObj4:MyMethod();
show("----------------Clling the method directly------------------------");
show("       1       ");
SampleObj1:MyChangeMethod(SampleObj1:F_Type,SampleObj1:ObjID);
show("       2       ");
SampleObj2:MyChangeMethod(SampleObj2:F_Type,SampleObj2:ObjID);
show("       3       ");
SampleObj3:MyChangeMethod(SampleObj3:F_Type,SampleObj3:ObjID);
show("       4       ");
SampleObj4:MyChangeMethod(SampleObj4:F_Type,SampleObj4:ObjID);

 

Craige_Hales
Super User

Re: Class inheritance, what am i missing?

Great questions. The doc, such as it is, does not cover it in the detail you are looking for...at least none that I've found.

I believe you have the correct intuitive understanding about how to override a method in the base class; assigning a new method in the derived init will work, for the same reason the F_Type works when F_Type is not "redeclared" in the derived class.

I also believe you are observing a bug, previously described by @hardner  https://community.jmp.com/t5/Uncharted/How-to-use-Define-Class/bc-p/694620/highlight/true#M384  which I've partly forgotten, but at the time I was convinced that sometimes an object pointer might be to the fully derived object and other times might be to the super: object.

@julian  - this kind of doc needs to be written by a developer that understands why a virtual method is useful.

Craige
peri_a
Level III

Re: Class inheritance, what am i missing?

Craige,

Thanks again for the very quick reply.

The more i play with this the more i am convinced that my original "override" of variable and method should be the way it should be and the current behavior should be modified.

 

Unfortunately i realized that adding the method in the _init_ is not really a useful solution for "multilevel" inheritance.

if a method should be naturally described in the "middle" class it would not be accessible by the lower levels as adding to the _init_ only will set it for the active object i create but will not act as prototype for the class deriving from it.

 

Same it should be the behavior also for the properties.  I think should be able to declare them without the need of the _Init_. _Init_ should be used only when the value is set at runtime and not predefined (my opinion), defining in the _Init_ would require me to specify it for all the derived classes not just on the level i need with more effort of maintenance

 

Let make the example of shapes. i have a general polygon to which you need to specify "sides" as input at runtime so there i would have it in the input and defined when the object is initiated. i expect that for quadrilateral i would hard code the "sides" to be 4. for the square however i would like inheriting from quadrilateral not defining it again to 4.

for sides is fine... but if i need to initialize a long list or an associative array, is annoying repeating it for each class.

 

What is your opinion?

Craige_Hales
Super User

Re: Class inheritance, what am i missing?

I've tried to use the classes several times and found some good use cases (pass-by-reference), some bugs, and some missing features (this).

I'm still refining my model for how it works, thanks for the questions and discussion.

  • I think it will help to think of each subclass as having a namespace-like set of member variables of its own and a hidden link to the next base class set of member variables. And, think of this as that namespace. You'd like for this to be the most derived class, because from there you can follow the links to the most base class and learn everything. What can go wrong, or at least against expectations, is sometimes when a method is called, this becomes the namespace where the method lives. There is no link back to the subclass. It won't matter, unless the method expects to call virtual functions in the subclass. So it will make a difference if the subclass used the _init_ to replace the virtual function in the base class (OK) or assigned it into the virtual function's namespace (Fails).
  • I'm pretty certain there is more copying going on than you are hoping for, and it is done to make the multi-level inheritance work. When a 3-level class is created, the base class is copied in, then the middle, then the most-derived. I believe the nested _init_ calls happen after that.

 

Craige