ネイティブメソッドを実装する |
すでにネイティブメソッドで Java オブジェクトへのハンドルを獲得したので、今度 はそれを使って何ができるかを考える。ハンドルを使用すると、オブジェクトのメンバ変数にアクセスすることができる 。ただし、まずハンドルの参照解除 を行う必要がある。ハンドルの参照解除
オブジェクトハンドルの参照解除を行うには、unhand()
マクロをオブジェクトハンドルに適用する。ハンドルを参照解除すると、Java オブジェクトの構造体と一致する C 言語のstruct
へのポインタが得られる。たとえば 、名前がbuffer
で、InputFile_read()
関に引き渡 されるバイト配列を参照解除するには、次のようにunhand()
マクロ を使用する。unhand(buffer)unhand()
は C 言語構造体へのポインタを返す。構造体の要素にアク セスするには標準的な C 言語構文を使用することができる。 構造体の要素はオブジェクトのメンバ変数と一致する。したがって、unhand()
が返すポインタによってオブジェクトのメンバ変数にアクセスすることができる。注:
unhand()
マクロはinterpreter.h
ヘッダーファイルに定義されており、ネイティブメソッドを実装する C言語ソースファイルにはこのヘッダーファイルがインクルードされていなければならない。このファイルは、StubPreamble.h
のインクルードを通してInputFileImpl.c
およびOutputFileImpl.c
の両方にインクルードされている。オブジェクトのメンバ変数のアクセス
C 言語のunhand()
から返るポインタは C 言語の他のstruct
の ポインタと同じように使用でき、->
によってそのメンバをアクセスすることができる。
InputFile_read
関数では、buffer
はバイト配列である。C 言語で Java 配列を割り当てる構造体にはbody
という名前の要素があり、これはC 言語の文字配列である。InputFile_read()
関数は、次のコードによって、buffer
のbody
要素へのポインタを獲得する。char *data = unhand(buffer)->body;InputFile_read()
関数は配列本体への C 言語ポインタを得て、 次のコード行のように簡単にバイトを読み取れる。actual = read(unhand(this)->fd, data, actual);struct
メンバは Java クラスで対応する変数と同じ名前をもっている 。このため、この同じメカニズムを利用して、InputFile オブジェクトのメンバ変数にアク セスすることができる。実際に、InputFile_open()
関数はこのメカニズムを利用して、InputFile オブジェクトのpath
とfd
変数 にアクセスしている。long InputFile_open(struct HInputFile *this) { int fd; char buf[MAXPATHLEN]; javaString2CString(unhand(this)->path, buf, sizeof(buf)); convertPath(buf); fd = open(buf, O_RDONLY); if (fd unhand(this)->fd = fd; return(TRUE); }struct
にある変数のデータ型は、オブジェクトのメンバ変数の Java タイプに最も一致する C 言語データ型であることに注意する。 構造体要素の宣言および使用は適切なデータ型で行わなければならない。表 に Java でのデータ型と C 言語でのデータ型との対応 を示す。
Java オブジェクトのメソッドをネイティブメソッドから呼び出すことはできるが、 この場合のメカニズムはメンバ変数をアクセスする場合とは異なっている。例として、次の コード片を考えてみる。これは機能しない。この目的には、interpreter.h で宣言されているユーティリティ関数の1つを使用する。ptr->methodname(); // 機能しない
execute_java_dynamic_method()
- ネイティブメソッドからインスタンスメソッドを呼び出す
execute_java_static_method()
- ネイティブメソッドからクラスメ ソッドを呼び出す
execute_java_constructor()
- ネイティブメソッドの中から新規の Java オブジェクトを作成する
ここで扱っている文字置換プログラムは上記のメソッドをどれも使用していないの で、実際に使用している別の例を見てみる。新しいプログラム例は、ネイティブメソ ッドの中からさまざまなタスクを実行する方法を示すメソッドを集めたものである。 ここでは、NativeExample クラスにある
doubleUp()
メソッドを取り上げる。doubleUp()
メソッドの実装は、execute_java_constructor()
を使用してネイティブメソッドの中からオブジェクトを作成した後、execute_java_dynamic_method()
を使用してそのオブジェクトのインスタンスメソッドの 1 つを呼び出している。このメソ ッドは作成したオブジェクトを返す。Java 側では、
doubleUp()
は次のように宣言されている。C 言語側では、native NativeExample doubleUp();doubleUp()
は次のように実装されている。struct HNativeExample * NativeExample_doubleUp(struct HNativeExample *hInst) { HNativeExample *hNewInst; long twoX; hNewInst = (HNativeExample *)execute_java_constructor( 0, "NativeExample", 0, "(I)", unhand(unhand(hInst)->myValue)->value); twoX = (long)execute_java_dynamic_method( 0, (HObject *)hNewInst, "twoTimes", "()I"); unhand(unhand(hNewInst)->myValue)->value = twoX; return hNewInst; }doubleUp()
関数の、この節の内容に関係の深い部分は太字で示してある。Java コンストラクタの呼び出し
上記のコードで最初の太字行である、execute_java_constructor()
へ の呼び出しを見てみよう。このexecute_java_constructor()
関数では 4 つの引数が必須であるが、それ以上の引数がある場合もある。
execute_java_constructor()
への最初の引数は 実行コンテキストである。ほとんどの場合、この引数の値には "0" を使用し、Java 実行時システムに現在の実行コンテキストを使用するよう指示する。2 番目の引数はインスタンス化したいクラスの名前である。ここの例では、新規に NativeExample オブジェクトを作成するので、2 番目の引数の値は "NativeExample" 文字列である。
3 番目の引数はインスタンス化したいクラスの構造である。この引数は、2番目の引数と同じくインスタンス化したいクラスについての情報を提供するため、余分である。しかし、
execute_java_constructor()
関数は、(例で示すように)2番目の引数にクラス名のみを使用し、3番目の引数に 0 を使用する場合、クラスをルックアップしなくてはならない。これは同じクラスからたくさんのオブジェクトを構築する場合のパフォーマンスを下げる。もし、パフォーマンスが重要な場合は、interpreter.h で定義されている FindClass() 関数を 3番目の引数の値に使用することにより、不必要なルックアップを避けることができる。4 番目の引数は呼び出したい Java コンストラクタを指定する。この引数は、
execute_java_constructor()
に渡す引数 がまだあるか、あるとすれば何個かを示す。
execute_java_constructor()
の一般的な書式は次のとおりである。コンストラクタへの引数の合計数はシグニチャ引数によって判別される。このシグニチャはどの classname コンストラクタが呼び出されるかも示す。この関数 は新規のオブジェクトを返すが、エラーがあった場合は 0 を返す。HObject *execute_java_constructor(ExecEnv *env, char *classname, ClassClass *cb, char *signature, ...);インスタンスメソッドの呼び出し
doubleUp()
関数は、execute_java_constructor()
に よって新規の NativeExample オブジェクトを作成した後、そのオブジェクトのtwoTimes()
インスタンスメソッドを呼び出す。twoX = (long)execute_java_dynamic_method( 0, (HObject *)hNewInst, "twoTimes", "()I");execute_java_dynamic_method()
関数は4 つの引数が必須であるが、 呼び出し中のメソッドによってはさらに多くの引数をもつ場合もある。
execute_java_dynamic_method()
の最初の引数は、execute_java_constructor()
の最初の引数と同じく Java 環境である 。この関数でも、現在の Java 環境を使用するよう Java 実行時システムに指示する "0" を指定するのがほとんどである。2 番目の引数は実行したいインスタンスメソッドが属するオブジェクトである。3 番目の引数は実行したいインスタンスメソッドの名前である。最後の 4 番目の引数は インスタンスメソッドのシグニチャである。
execute_java_constructor()
の場合と同様、シグニチャ引数は残っている引数があるかどうか、あるとすればその個数と型を示す。execute_java_dynamic_method()
のシグニチャ引数は、前にexecute_java_constructor()
で行っ たのと同じように表される。
execute_java_dynamic_method()
の一般的な書式は次のとおりである 。long execute_java_dynamic_method(ExecEnv *env, HObject *obj, char *method_name, char *signature, ...);クラスメソッドの呼び出し
ネイティブメソッドからクラスメソッドを呼び出すときは、次のようなコードとなる 。この関数は、オブジェクトの代わりにクラスを指定することを除けば、先に示したlong execute_java_static_method(ExecEnv *env, ClassClass *cb, char *method_name, char *signature, ...);execute_java_dynamic_method()
によく似ている。
ネイティブメソッドを実装する |