相互に継承する一連のクラスを作成しているときに問題が発生しました。
わかりやすくするために、コードを以下の基本的な例に取り除きました。
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).
インスタンス化中、およびオブジェクト自体からプロパティにアクセスしようとしたときも、F_Type が正しく 3 に設定されていることがわかります。
ただし、メソッド内にいるとき、 this:F_Type または F_Type の値は何らかの理由で 2 になります (ただし、オブジェクトを格納する連想配列からアクセスしている場合、値は正しく 3 です)。奇妙なことに、このメソッドは Base クラス (F_Type= 1 であるため、値が 2 である理由が理解できません) で定義されています。これは何らかのタイプのバグでしょうか? (これは JMP 17.1.0 と JMP 18 Early の両方で見られます)リリースですが、テストできる以前のバージョンはありません)
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
<BR />// 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;
);
<BR />// start of the actual script
SampleObj=MyNameSp:CreateNewObj("MyDoubleDerived","Key1");
show("value From script");
show(SampleObj:F_Type);
SampleObj:MyMethod();
再確認しましたが、同じ動作がメソッドにも当てはまります。
特定のメソッドを別のメソッドから呼び出し、「ターゲット」メソッドの継承レベルが異なる場合、スタック内の「1 レベル上の」クラスのメソッドが使用されます (表現が意味をなすかどうかはわかりません)。
同様に、「_init_」メソッド内から派生クラスのメソッドを定義すると、すべて期待どおりに動作します
変数の場合は、「_init_」メソッドで行うロジックは理解できますが、メソッドの場合は直観に反しているように思えます。
それに関する文書はありますか?
ここに変更された例があります:
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);</CODE></P><P> </P></LINGO-BODY><LINGO-SUB id="lingo-sub-725797" slang="en-US" mode="CREATE">Re: クラス継承、何が足りないのでしょうか?</LINGO-SUB><LINGO-BODY id="lingo-body-725797" slang="en-US" mode="CREATE"><P>返信いただきありがとうございます。</P><P>コードを読むのは簡単なので作業を続けることができますが、カーテンの後ろに何があるかはよくわかりません。</P><P>プロパティを直接呼び出しているときは、正しい値(3)が得られます。値が2(1ではなく)であるのはメソッド内からのみです。また、別のレベルを追加しようとしました(常にn-1レベルです)。考えられている)。</P><P> </P><P>さらに、変数に格納されているメソッドの継承はどうなるのかという別の疑問が生じます...「_init_」内から特定のメソッドも定義する必要がありますか?</P><P> </P><P> </P></LINGO-BODY><LINGO-SUB id="lingo-sub-725714" slang="en-US" mode="CREATE">Re: クラス継承、何が足りないのでしょうか?</LINGO-SUB><LINGO-BODY id="lingo-body-725714" slang="en-US" mode="CREATE"><P>これはおそらくあなたが望むものに近いです</P>
<PRE><CODE class=" language-jsl">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;
派生クラスは super:_init_ を使用して、基本クラスが完全に構築されていることを確認してから、派生クラスの初期化を続けます。各層での F_Type の再定義は、基本クラス F_Type を置き換えるのではなく、非表示にします。あなたの例では、メソッド呼び出し内で F_Type=1 ではなく F_Type=2 が表示される理由がわかりませんが、上記の使用パターンでは問題は解決します。
すばらしい質問です。このドキュメントは、このままでは、あなたが探している詳細についてはカバーしていません...少なくとも私が見つけたものではありません。
基本クラスのメソッドをオーバーライドする方法について、直感的に正しく理解できていると思います。派生クラスで F_Type が「再宣言」されていない場合でも F_Type が機能するのと同じ理由で、派生 init で新しいメソッドを割り当てると機能します。
また、 @hardner https://community.jmp.com/t5/Uncharted/How-to-use-Define-Class/bc-p/694620/highlight/true#M384によって以前に説明されたバグを観察していると思います。一部忘れていましたが、当時は、オブジェクト ポインタが完全に派生したオブジェクトを指す場合もあれば、super: オブジェクトを指す場合もある、と確信していました。
@julian - この種のドキュメントは、仮想メソッドが役立つ理由を理解している開発者によって作成される必要があります。
クレイグ
とても素早いご返信ありがとうございます。
これをいじればいじるほど、変数とメソッドの元の「オーバーライド」は本来あるべきものであり、現在の動作を変更する必要があると確信します。
残念ながら、_init_ にメソッドを追加することは、「マルチレベル」継承にとって実際には有用な解決策ではないことに気付きました。
メソッドが自然に「中間」クラスで記述される必要がある場合、_init_ に追加するだけで、作成したアクティブなオブジェクトにメソッドが設定されますが、そこから派生するクラスのプロトタイプとして機能しないため、下位レベルからはアクセスできません。
プロパティについても同様に動作する必要があります。 _Init_ を必要とせずに宣言できるはずだと思います。 _Init_ は、値が実行時に設定され、事前定義されていない場合にのみ使用する必要があります (私の意見)。 _Init_ で定義すると、必要なレベルだけでなく、すべての派生クラスに対してそれを指定する必要があり、メンテナンスにさらに労力がかかります。
形状の例を作ってみましょう。実行時に入力として「側面」を指定する必要がある一般的なポリゴンがあるので、それを入力に含めて、オブジェクトの開始時に定義します。四角形の場合、「辺」が 4 になるようにハードコードすることを期待しています。ただし、正方形の場合は、四角形を再度 4 に定義せずに継承したいと考えています。
for Sides は問題ありません...しかし、長いリストや連想配列を初期化する必要がある場合、クラスごとにそれを繰り返すのは面倒です。
あなたの意見は何ですか?
私はクラスを何度か使用しようとしましたが、いくつかの良い使用例 (参照渡し)、いくつかのバグ、およびいくつかの不足している機能 (これ) を見つけました。
モデルがどのように機能するかについてはまだ改良中です。質問や議論に感謝します。
- 各サブクラスには、名前空間のような独自のメンバー変数のセットと、次の基本クラスのメンバー変数セットへの隠しリンクがあると考えるとわかりやすいと思います。そして、これをその名前空間と考えてください。ここから最も基本的なクラスへのリンクをたどってすべてを学習できるため、これを最も派生したクラスにしたいと考えています。問題が発生する可能性がある、または少なくとも期待に反して、メソッドが呼び出されるときに、これがメソッドが存在する名前空間になる場合があります。サブクラスへのリンクはありません。メソッドがサブクラス内の仮想関数を呼び出すことを想定していない限り、これは問題ではありません。したがって、サブクラスが _init_ を使用して基本クラス内の仮想関数を置き換えるか (OK)、それを仮想関数の名前空間に割り当てたか (失敗) が異なります。
- あなたが期待しているよりも多くのコピーが行われていると確信していますが、これはマルチレベルの継承を機能させるために行われています。 3 レベルのクラスが作成されると、基本クラスがコピーされ、次に中間クラス、最後に派生したクラスがコピーされます。その後、ネストされた _init_ 呼び出しが発生すると思います。