メソッドの定義と呼び出し
ここでは、InterSystems IRIS® データ・プラットフォームのクラスのメソッドを作成する際の規則とオプションについて説明します。また、そのようなメソッドを呼び出す際の規則とオプションについても説明します。
インスタンス・メソッドはオブジェクト・クラスにのみ適用され、ここでは説明していません。
メソッドの概要
メソッドとは、クラスによって定義される実行可能な要素です。InterSystems IRIS は、インスタンス・メソッドとクラス・メソッドの 2 種類のメソッドをサポートしています。インスタンス・メソッドは、クラスの特定のインスタンスから呼び出され、通常はそのインスタンスに関連するアクションを実行します。クラス・メソッドとは、オブジェクト・インスタンスへの参照なしに呼び出せるメソッドのことです。その他の言語では、静的メソッドと呼ばれています。
通常、メソッドは、インスタンス・メソッドを指します。それよりも具体的な表現のクラス・メソッドは、クラス・メソッドを表すために使用します。
インスタンス・メソッドは、オブジェクトのインスタンスが存在しないと実行できません。そのため、インスタンス・メソッドは、オブジェクト・クラスで定義されている場合にのみ役立ちます。これに対して、クラス・メソッドは、どの種類のクラスで定義してもかまいません。
メソッドの定義
クラスにクラス・メソッドを追加するには、クラス定義に以下に示すような要素を追加します。
ClassMethod MethodName(Arguments) as Classname [ Keywords]
{
//method implementation
}
以下はその説明です。
-
MethodName は、メソッドの名前です。ルールについては、"名前付け規約" を参照してください。
-
Arguments は、引数のコンマ区切りリストです。詳細は、"メソッドの引数の指定: 基本" を参照してください。
-
Classname は、このメソッドで返される値 (ある場合) のタイプを表す、オプションのクラス名です。メソッドが値を返さない場合は、As Classname 部分を省略します。
クラスは、データ型クラス、オブジェクト・クラス、またはタイプのないクラス (あまり一般的ではありません) になります。クラス名は完全修飾クラス名か、短いクラス名になります。詳細は、"クラス参照時のパッケージの使用" を参照してください。
-
Keywords はメソッド・キーワードを表します。これはオプションです。"コンパイラ・キーワードの概要" を参照してください。
-
メソッドの実装は、メソッドの実装言語とタイプによって異なります。"実装言語の指定" および "メソッドのタイプ" を参照してください。既定では、メソッドの実装はゼロ行以上の ObjectScript で構成されます。
クラスにインスタンス・メソッドを追加するには、同じ構文で ClassMethod の代わりに Method を使用します。
Method MethodName(arguments) as Classname [ Keywords]
{
//method implementation
}
インスタンス・メソッドは、オブジェクト・クラスでのみ存在価値があります。
実装言語の指定
メソッドを作成するときには、実装言語を選択できます。実際には、1 つのクラス内で、複数のメソッドを異なる言語で実装することもできます。すべてのメソッドは、実装言語とは関係なく相互運用します。メソッドの記述に使用する言語を指定するには、以下の構文を使用します。
Method MyMethod() [ Language = objectscript ]
{
// implementation details written in ObjectScript
}
Method MyMethod() [ Language = python ]
{
# implementation details written in Python
}
前述の例のように、メソッドの言語はすべて小文字で記述する必要があります。
メソッドで Language キーワードを使用しない場合、コンパイラは、メソッドが所属するクラスの Language キーワードで指定された言語で記述されていると想定します。このキーワードについては、既定が objectscript (ObjectScript) で、他に python、tsql、および ispl を使用できます。詳細は、"Language (メソッド・キーワード)" を参照してください。
メソッドの引数の指定: 基本
メソッドは、任意の数の引数を取ることができます。メソッドの定義では、メソッドが取る引数を指定する必要があります。また、各引数のタイプと既定値も指定できます。(この場合のタイプは、あらゆる種類のクラスを指します。特にデータ型クラスを指すわけではありません。)
以下の汎用クラスのメソッド定義について考えてみます。
ClassMethod MethodName(Arguments) as Classname [ Keywords]
{
//method implementation
}
Arguments は、以下に示す一般的な形式になります。
argname1 as type1 = value1, argname2 as type2 = value2, argname3 as type3 = value3, [and so on]
以下はその説明です。
-
argname1、argname2、argname3 などは、引数の名前です。これらの名前は、変数名の規則に従う必要があります。
-
type1、type2、type3 などは、クラス名です。メソッド定義のこの部分は、このメソッドを使用することになるプログラマに向けて、どのタイプの値を対応する引数に渡すかについて説明することを目的としています。通常は、各メソッド引数のタイプを明示的に指定することをお勧めします。
一般に、このタイプはデータ型クラスかオブジェクト・クラスになります。
クラス名は完全修飾クラス名か、短いクラス名になります。詳細は、"クラス参照時のパッケージの使用" を参照してください。
構文のこの部分は省略できます。その場合は、as 部分も省略します。
-
value1、value2、value3 などは、引数の既定値です。引数に値が指定されていない状態でメソッドが呼び出されると、引数はメソッドでこの値に自動的に設定されます。
各値は、リテラル値 ("abc" や 42)、または中括弧で囲まれた ObjectScript 式のどちらかにできます。例えば、以下のようになります。
ClassMethod Test(flag As %Integer = 0) { //method implementation }
別の例を示します。
ClassMethod Test(time As %Integer = {$horolog} ) { //method implementation }
構文のこの部分は省略できます。その場合は、等号 (=) も省略します。
例えば、3 つの引数を取る Calculate() メソッドを考えてみます。
ClassMethod Calculate(count As %Integer, name, state As %String = "CA")
{
// ...
}
ここで、count と state はそれぞれ %IntegerOpens in a new tab、%StringOpens in a new tab として宣言されます。引数のデータ型の既定は %StringOpens in a new tab なので、タイプが指定されていない場合は %StringOpens in a new tab になります。上記では、name がその例です。
引数の渡し方の指示
メソッド定義では、このメソッドを使用することになるプログラマに向けて、各引数の予期された渡し方についても指示します。引数は、値 (既定の方法) または参照によって渡すことができます。
引数を参照で渡す必要があることを指示するには、引数名の前方のメソッド・シグニチャに ByRef 修飾子を含めます。両方の引数に ByRef を使用する例を以下に示します。
/// Swap value of two integers
Method Swap(ByRef x As %Integer, ByRef y As %Integer)
{
Set temp = x
Set x = y
Set y = temp
}
同様に、引数を参照で渡す必要があることに加えて、値を受け取らないことを指示するには、引数名の前方のメソッド・シグニチャに Output 修飾子を含めます。以下に例を示します。
Method CreateObject(Output newobj As MyApp.MyClass) As %Status
{
Set newobj = ##class(MyApp.MyClass).%New()
Quit $$$OK
}
ByRef および Output キーワードは、インターシステムズ・クラス・リファレンスの利用者全員に役立つ情報を提供します。これらのキーワードは、コードの動作には影響しません。メソッドの呼び出し方法に関する規則を適用するのは、メソッドの作成者の責任です。
ObjectScript で引数を参照渡しするには、そのメソッドを呼び出すときに変数名の前にピリオドを付けます。Python では、渡す値に対して iris.ref() を使用し、その参照に対してメソッドを呼び出します。これら両方を以下の例に示します。
Do MyMethod(arg1, .arg2, .arg3)
arg2=iris.ref("peanut butter")
arg3=iris.ref("jelly")
MyMethod(arg1,arg2,arg3)
ObjectScript でこのような引数を渡す方法の詳細は、"参照渡しまたは出力引数" を参照してください。
引数のスキップ
メソッドに適切な既定値がある場合、メソッドを呼び出すときに引数をスキップできます。ObjectScript と Python にはそれぞれ、引数をスキップするための独自の構文があります。
ObjectScript で引数をスキップするには、引数の値を指定せずにコンマ構造を維持します。例えば、以下は有効です。
set myval=##class(mypackage.myclass).GetValue(,,,,,,4)
InterSystems IRIS クラスでは、Python メソッドのシグニチャで最初に必須の引数を記述し、その後に引数と既定値を記述する必要があります。そのメソッドを呼び出す際に、引数をメソッドのシグニチャの順番で指定する必要があります。したがって、1 つの引数をスキップした場合、その後の引数もすべてスキップする必要があります。例えば、以下は有効です。
ClassMethod Skip(a1, a2 As %Integer = 2, a3 As %Integer = 3) [ Language = python ]
{
print(a1, a2, a3)
}
TESTNAMESPACE>do ##class(mypackage.myclass).Skip(1)
1 2 3
可変個数の引数の指定
可変個数の引数を受け入れるメソッドを定義できます。これを行うには、以下の例に示すように、メソッド・シグニチャ内の最後の引数の名前の後ろに ... を含めます。
ClassMethod MultiArg(Arg1... As %List) [ Language = objectscript ]
{
Set args = $GET(Arg1, 0)
Write "Invocation has ",
args,
" element",
$SELECT((args=1):"", 1:"s"), !
For i = 1 : 1 : args
{
Write "Argument[", i , "]: ", $GET(Arg1(i), "<NULL>"), !
}
}
ClassMethod MultiArg(Arg1... As %List) [ Language = Python ]
{
print("Invocation has", len(Arg1), "elements")
for i in range(len(Arg1)):
print("Argument[" + str(i+1) + "]: " + Arg1[i])
}
この場合に引数を渡す方法の詳細は、"可変個数の引数の引き渡し" を参照してください。
既定値の指定
ObjectScript または Python メソッドで引数の既定値を指定するには、以下の例に示す構文を使用します。
Method Test(flag As %Integer = 0)
{
//method details
}
メソッドが呼び出されるとき、引数が指定されていない場合は既定値を使用します (既定値が指定されている場合)。メソッドが Python で記述されている場合、既定値を持つ引数は、引数リストの末尾に定義する必要があります。
ObjectScript では、$GET 関数を使用して既定値を設定することもできます。以下に例を示します。
Method Test(flag As %Integer)
{
set flag=$GET(flag,0)
//method details
}
ただし、この方法ではクラス・シグニチャに影響しません。
値を返す方法
式メソッドの場合を除き、値を返すようにメソッドを定義するには、そのメソッド内で以下のいずれかを使用します (ObjectScript でメソッドを実装する場合)。
Return returnvalue
または
Quit returnvalue
returnvalue は、このメソッドが返す適切な値です。これは、宣言したメソッド返りタイプと一致する必要があります。返りタイプがデータ型クラスの場合、メソッドはリテラル値を返す必要があります。返りタイプがオブジェクト・クラスの場合、メソッドは、そのクラスのインスタンス (具体的には、OREF) を返す必要があります。詳細は、"登録オブジェクトを使用した作業" を参照してください。
例 :
ClassMethod Square(input As %Numeric) As %Numeric
{
Set returnvalue = input * input
Return returnvalue
}
もう 1 つ例を挙げると、このメソッドはオブジェクト・インスタンスを返します。
ClassMethod FindPerson(id As %String) As Person
{
Set person = ##class(Person).%OpenId(id)
Return person
}
値を返す構文は、メソッドの実装言語によって異なります。
継承されたメソッドのオーバーライド
クラスは、その 1 つまたは複数のスーパークラスからメソッド (クラスおよびインスタンス・メソッドの両方) を継承し、それらはオーバーライドできます。その場合、自身のメソッド定義のシグニチャが、オーバーライドするメソッドのシグニチャと一致していることを確認する必要があります。サブクラス・メソッドの各引数は、スーパークラス・メソッドの引数と同じデータ型を使用するか、そのデータ型のサブクラスを使用する必要があります。ただし、サブクラスのメソッドで、そのスーパークラスに定義されていない追加の引数を指定することはできます。
メソッドのシグニチャが一致する限り、ObjectScript で記述されているメソッドを Python メソッドでオーバーライドすることも、その逆を行うこともできます。
サブクラスのメソッド内で、スーパークラス内のそれがオーバーライドしたメソッドを参照できます。ObjectScript でそれを行うには、##super() 構文を使用します。以下に例を示します。
//overrides method inherited from a superclass
Method MyMethod() [ Language = objectscript ]
{
//execute MyMethod as implemented in the superclass
do ##super()
//do more things....
}
##super は、大文字と小文字を区別しません。
特権チェックを使用したアクセスの制限
Requires キーワードを使用して、メソッドへのアクセスを制限できます。指定された特権を持っているユーザまたはプロセスのみがメソッドを呼び出すことができます。
特権を指定するには、リソースの名前と適切なレベルの許可 (Use、Read、または Write) をコロンで区切って指定します。複数の特権を指定するには、コンマ区切りリストを使用します。
以下の例では、MyAction メソッドは Service_FileSystem リソースに対する Use 許可を必要とします。
ClassMethod MyAction() [ Requires = Service_FileSystem: Use ]
{
write "You have access to this method."
}
必要な特権がない場合にこのメソッドを呼び出すと、<PROTECT> エラーが発生します。
<PROTECT> *Method MyAction' requires resource 'Service_FileSystem: Use'
メソッドがスーパークラスから Requires キーワードを継承する場合、キーワードに新しい値を設定して、必要な特権のリストに追加できます。この方法で必要な特権を削除することはできません。
詳細は、"特権および許可" を参照してください。また、"Requires" キーワードのリファレンス情報も参照してください。
メソッドのタイプ (CodeMode オプション)
InterSystems IRIS では、クラス・コンパイラによる処理が異なる 4 つのタイプのメソッドをサポートしています。
コード・メソッド
コード・メソッドは、その実装が単純なコード行であるメソッドです。これは最も一般的なメソッドのタイプで、既定です。
メソッド実装には、実装言語に応じた有効なコードを含めることができます。
InterSystems IRIS には、多用する簡単なタスクを実行する一連のシステム定義メソッドが用意されています。ユーザ定義メソッドでこれらのタスクのいずれかを実行するようにした場合、コンパイラではそのユーザ定義メソッドの実行可能コードが生成されません。その代わり、このようなユーザ定義メソッドは該当のシステム定義メソッドに関連付けられます。このユーザ定義メソッドを呼び出すと、関連付けられたシステム定義メソッドが呼び出されるので、パフォーマンスの向上が望めます。また、デバッガはそのようなシステム定義メソッドのステップ実行を行いません。
式メソッド
式メソッドは、実装が式のみで構成されるメソッドです。実行されると、このメソッドはその式の値を返します。式メソッドは一般的に、高速な実行を必要とする (データ型クラス内の) 単純なメソッドに使用されます。
例えば、前の例の Dog クラスの Speak() メソッドを、式メソッドに変換することが可能です。
Method Speak() As %String [CodeMode = expression]
{
"Woof, Woof"
}
dog が Dog オブジェクトを参照しているとすると、以下のようにこのメソッドを使用できます。
Write dog.Speak()
これは、以下のコードを生成する結果となります。
Write "Woof, Woof"
式メソッドのすべての仮変数に、既定値を与えることをお勧めします。これは、実行時に実際の変数が見つからないことで発生する、潜在的な置換問題を防ぎます。
InterSystems IRIS は、式メソッド内のマクロや参照渡し引数を許可していません。
呼び出しメソッド
呼び出しメソッドは、既存の InterSystems IRIS ルーチンの周囲のメソッド・ラッパを作成する、特別なメカニズムです。これは一般的に、従来のコードをオブジェクト・ベースのアプリケーションに移行するときに便利です。
メソッドを呼び出しメソッドとして定義すると、メソッドが呼び出されるときは常に、指定されたルーチンが呼び出されます。呼び出しメソッドの構文は以下のとおりです。
Method Call() [ CodeMode = call ]
{
Tag^Routine
}
Tag^Routine は、ルーチン内のタグ名を指定します。
メソッド・ジェネレータ
メソッド・ジェネレータは実際には、クラスのコンパイル中にクラス・コンパイラによって呼び出される小さなプログラムです。この出力は、メソッドの実際の実行時実装です。メソッド・ジェネレータは強力なクラス継承の方法を提供し、クラスやプロパティ継承の必要性に応じてカスタマイズされた、高性能で特別なコードを生成します。InterSystems IRIS ライブラリ内で、メソッド・ジェネレータは、データ型やストレージ・クラスによって広範囲に使用されます。
詳細は、"メソッド・ジェネレータとトリガ・ジェネレータの定義" を参照してください。
メソッドを SQL ストアド・プロシージャとして投影する方法
クラス・メソッド (インスタンス・メソッドを除く) は、SQL ストアド・プロシージャとしても使用できるように定義できます。そのためには、メソッド定義に SqlProc キーワードを含めます。
プロシージャの既定の名前は CLASSNAME_METHODNAME になります。別の名前を指定するには、SqlName キーワードを指定します。
詳細は、"ストアド・プロシージャの定義" を参照してください。
クラス・メソッドの呼び出し
このセクションでは、ObjectScript のクラス・メソッドを呼び出す方法について説明します。このセクションは、あらゆる種類のクラスに適用されます。インスタンス・メソッドはオブジェクト・クラスにのみ適用され、ここでは説明していません。
-
任意のクラスのクラス・メソッドを呼び出すには (そのメソッドがプライベートでない場合)、以下の式を使用します。
##class(Package.Class).Method(Args)
Package.Class はクラスの名前、Method はメソッドの名前、Args はメソッドの引数です。
##class は、大文字と小文字を区別しません。
この式は指定されたクラス・メソッドを呼び出し、その返り値を取得します (ある場合)。この式は、DO コマンドや SET コマンドなどと共に使用することも、別の式の一部として使用することもできます。以下に、いくつかのバリエーションを示します。
do ##class(Package.Class).Method(Args) set myval= ##class(Package.Class).Method(Args) write ##class(Package.Class).Method(Args) set newval=##class(Package.Class).Method(Args)_##class(Package2.Class2).Method2(Args)
パッケージは省略できます。その場合は、使用に適したパッケージ名がクラス・コンパイラによって判断されます (この名前解決は、"パッケージ" で説明されています)。
-
(クラス・メソッド内で) 以下の式を使用して、そのクラスの別のクラス・メソッド (継承されたメソッドでもかまいません) を呼び出します。
..MethodName(args)
この式は DO コマンドと共に使用できます。メソッドが値を返す場合は、SET を使用することも、別の式の一部として使用することもできます。以下に、いくつかのバリエーションを示します。
do ..MethodName() set value=..MethodName(args)
Note:この構文をクラス・メソッドで使用してプロパティやインスタンス・メソッドを参照することはできません。これらを参照するにはインスタンス・コンテキストが必要になります。
-
実行時までメソッド名が決定されないクラス・メソッドを実行するには、以下のように ObjectScript の $CLASSMETHOD 関数を使用します。
$CLASSMETHOD(classname, methodname, Arg1, Arg2, Arg3, ... )
classname は、クラスの完全修飾名に評価されます。methodname は、そのクラス内のクラス・メソッドの名前に評価されます。また、Arg1、Arg2、Arg3 などは、そのメソッドへの引数になります。例えば、以下では Sample.Person クラスから PrintPersons メソッドを実行します。
set cls="Sample.Person" set clsmeth="PrintPersons" do $CLASSMETHOD(cls,clsmeth)
指定されたメソッドが存在しない場合や、そのメソッドがインスタンス・メソッドの場合は、システムによって <METHOD DOES NOT EXIST> エラーが生成されます。指定されたメソッドがプライベートの場合は、システムによって <PRIVATE METHOD> エラーが生成されます。
メソッドに引数を渡す方法
メソッドに引数を渡す既定の方法は、値渡しです。この方法では、前述の例に示したように、変数、リテラル値、またはその他の式として、メソッド呼び出しに単に引数を含めます。
また、引数を参照で渡すことも可能です。
その動作を説明します。システムには、各ローカル変数の値を格納するためのメモリ位置があります。変数の名前は、メモリ位置のアドレスとして機能します。メソッドにローカル変数を渡すときには、値で変数を渡します。つまり、システムによってその値のコピーが作成され、元の値が変更されることはありません。その代わりに、メモリ・アドレスを渡すことができます。この方法は、参照渡しと呼ばれています。これは、引数として多次元配列を渡す唯一の方法でもあります。
ObjectScript の場合、引数を参照で渡すには、その引数の前にピリオドを置きます。以下に例を示します。
set MyArg(1)="value A"
set MyArg(2)="value B"
set status=##class(MyPackage.MyClass).MyMethod(.MyArg)
この例では、値 (多次元配列) を参照で渡すことで、その値をメソッドが受け取れるようにしています。その他の場合でも、引数を参照で渡すことが役立ちます。これは、メソッドの実行後に、その値を使用できるようになるためです。以下に例を示します。
set status=##class(MyPackage.MyClass).GetList(.list)
//use the list variable in subsequent logic
その他の場合、変数に値を指定し、その値を変更する (その値を参照で返す) メソッドを呼び出して、変更された値を使用することもできます。
メソッドのキャスト
あるクラスのメソッドを、別のクラスのメソッドとしてキャストするには、以下のどちらかの構文を使用します (ObjectScript の場合)。
Do ##class(Package.Class1)Class2Instance.Method(Args) Set localname = ##class(Package.Class1)Class2Instance.Method(Args)
クラス・メソッドとインスタンス・メソッドの両方ともキャストできます。
例えば、2 つのクラス MyClass.Up と MyClass.Down が、両方とも Go() メソッドを持つとします。 MyClass.Up の場合は、このメソッドが以下のようになります。
Method Go()
{
Write "Go up.",!
}
MyClass.Down の場合は、Go() メソッドが以下のようになります。
Method Go()
{
Write "Go down.",!
}
ユーザは、MyClass.Up のインスタンスを生成することができ、これを使用して MyClass.Down.Go メソッドを呼び出します。
>Set LocalInstance = ##class(MyClass.Up).%New() >Do ##class(MyClass.Down)LocalInstance.Go() Go down.
以下のように、式の一部に ##class を使用することもできます。
Write ##class(Class).Method(args)*2
変数を、返り値と同じ値に設定する必要はありません。
より一般的な方法は、インスタンスには $METHOD 関数を使用し、クラス・メソッドには $CLASSMETHOD 関数を使用することです。
継承されたメソッドの上書き
クラスは、その 1 つまたは複数のスーパークラスからメソッド (クラスおよびインスタンス・メソッドの両方) を継承します。Final の指定があるメソッドを除くと、それらの定義は、このクラス内に定義を用意することでオーバーライドできます。その場合は、以下の規則に注意してください。
-
メソッドがスーパークラスのクラス・メソッドの場合、サブクラスでインスタンス・メソッドとしてオーバーライドできません (逆の場合も同じです)。
-
サブクラス・メソッドの返り値の型は、元の返り値の型または元の返り値の型のサブクラスのいずれかと同じでなければなりません。
-
サブクラスのメソッドは、スーパークラスのメソッドより多くの引数を持つことができます。(さらに、"引数の数" も参照してください。)
-
サブクラス内のメソッドは、引数のための異なる既定値を指定できます。
-
サブクラス・メソッド内の引数の型は、元のメソッドの引数の型と一致していなければなりません。特に、指定するすべての引数は、元の型または元の型のサブクラスのいずれかと同じでなければなりません。
引数に指定された型がない場合には、コンパイラは引数を %StringOpens in a new tab として扱うことに注意してください。したがって、スーパークラスのメソッドの引数に型がない場合、サブクラスのメソッドの対応する引数は、%StringOpens in a new tab になるか、%StringOpens in a new tab のサブクラスになるか、または型なしになる可能性があります。
-
サブクラスのメソッドは、スーパークラスのメソッドと同じ方法で引数値を受け取る必要があります。例えば、指定した引数がスーパークラス内で参照によって渡される場合、同じ引数をサブクラス内でも参照によって渡す必要があります。
この点でメソッド・シグニチャに整合性がないと、他の開発者がメソッドの適切な使用方法を知ることは難しくなります。ただし、コンパイラはエラーを発行しないことに注意してください。
メソッドの実装が、スーパークラスで定義されているメソッドと同じ名前のメソッドを呼び出す必要がある場合、##super() 構文を使用できます。これについては、サブセクションで説明します。この説明は、ObjectScript で記述されたコードに適用されます。
##super()
メソッド内で以下の式を使用して、最も近接しているスーパークラスで定義されたメソッドと同じ名前のメソッドを呼び出します。
##super()
この式は DO コマンドと共に使用できます。メソッドが値を返す場合は、SET を使用することも、別の式の一部として使用することもできます。以下に、いくつかのバリエーションを示します。
do ##super()
set returnvalue=##super()_"additional string"
##super は、大文字と小文字を区別しません。
これは、スーパークラスの既存のメソッドを呼び出して、いくつかの追加手順を実行 (そのメソッドから返された値の変更など) する必要のあるメソッドを定義する場合に役立ちます。
##super と メソッドの引数
##super は、引数を許可するメソッドでも機能します。サブクラス・メソッドで引数の既定値を指定していない場合、スーパークラスへの参照で引数をメソッドが渡していることを確認してください。
例えば、スーパークラス (MyClass.Up.SelfAdd()) のメソッドのコードが以下のとおりだとします。
ClassMethod SelfAdd(Arg As %Integer)
{
Write Arg,!
Write Arg + Arg
}
出力は、以下のようになります。
>Do ##Class(MyClass.Up).SelfAdd(2) 2 4 >
サブクラス (MyClass.Down.SelfAdd()) のメソッドでは、##super を使用し、引数を参照で渡します。
ClassMethod SelfAdd(Arg1 As %Integer)
{
Do ##super(.Arg1)
Write !
Write Arg1 + Arg1 + Arg1
}
出力は、以下のようになります。
>Do ##Class(MyClass.Down).SelfAdd(2) 2 4 6 >
MyClass.Down.SelfAdd() では、引数名の前にあるピリオドに注目してください。これを省略して、引数を指定せずにメソッドを呼び出すと、<UNDEFINED> エラーを受け取ることになります。
##super が作用する呼び出し
##super は、現在のメソッド・コールにのみ作用します。そのメソッドで他の呼び出しを実行する場合、それらの呼び出しは、スーパークラスに対してではなく、現在のオブジェクトまたはクラスに対して実行されます。例えば、以下のように、MyClass.Up に MyName() メソッドと CallMyName() メソッドがあるとします。
Class MyClass.Up Extends %Persistent
{
ClassMethod CallMyName()
{
Do ..MyName()
}
ClassMethod MyName()
{
Write "Called from MyClass.Up",!
}
}
また、以下のように、MyClass.Down で上記のメソッドをオーバーライドするとします。
Class MyClass.Down Extends MyClass.Up
{
ClassMethod CallMyName()
{
Do ##super()
}
ClassMethod MyName()
{
Write "Called from MyClass.Down",!
}
}
ここで、CallMyName() メソッドを呼び出すと、以下のような結果が返されます。
USER>d ##class(MyClass.Up).CallMyName()
Called from MyClass.Up
USER>d ##class(MyClass.Down).CallMyName()
Called from MyClass.Down
MyClass.Down.CallMyName() の出力は、MyClass.Up.CallMyName() とは異なっています。これは、MyClass.Down.CallMyName() の CallMyName() メソッドには ##super が含まれており、MyClass.Up.CallMyName() メソッドを呼び出した後、キャストされない MyClass.Down.MyName() メソッドを呼び出しているからです。
引数の数
場合によっては、新しい引数をスーパークラス内のメソッドに追加して、サブクラス内のメソッドで定義されているよりも引数の数を多くすることが必要な場合もあります。コンパイラが (便宜上の理由で) サブクラス内のメソッドに引数を追加するために、サブクラスは引き続きコンパイルします。たいていの場合、メソッドを拡張するすべてのサブクラスを調べ、追加の引数を構成するシグニチャを編集し、コードを編集するかどうかを決定することも引き続き必要です。シグニチャまたはコードを編集しない場合でも、次の 2 つの点を考慮する必要があります。
-
追加の引数名が、サブクラス内のメソッドで使用されているどの変数の名前とも同じでないことを確認します。コンパイラは追加された引数をサブクラス内のメソッドに追加します。これらの引数が、サブクラスのメソッドで使用されている変数と同じ名前を持っていると、予期しない結果が生じることがあります。
-
サブクラス内のメソッドが (そのメソッドが ##super を使用しているために) 追加の引数を使用する場合は、スーパークラス内のメソッドが追加の引数に対して既定値を指定していることを確認します。