ネイティブメソッドを実装する |
InputFile_close()
およびOutputFile_close()
関数は どちらも 引数を1 つ受け入れる。この引数は、Java 実行時システムにより自動的に関数に渡される自動パラメータである。まず、// in InputFileImpl.c void InputFile_close(struct HInputFile *this) . . . // in OutputFileImpl.c void OutputFile_close(struct HOutputFile *this) . . .InputFile_close()
関数のシグニチャにおける自動パラメータの宣言に注目する。InputFile オブジェクト、すなわち、呼び出されているInputFile_close()
が属するオブジェクトは、構造体に対する ハンドル を通してネイティブメソッドに引き渡される。構造体の名前は、大文字 の 'H' ("Handle" を表すと思われる) と Java クラスの名前で構成される。このハンドルを使用すると、ネイティブメソッドの内部からオブジェクトのメンバ変 数を参照することができる。詳細については、ネイティブメソッドで Java オブジェクトを使用する で説明する。
引数として何らかのオブジェクトを受け入れるネイティブメソッドを書く場合は、そのオブジェクトも構造体へのハンドルとしてネイティブメソッドに引き渡されることに注意する。これは、実行時システムが自動パラメータを渡すときの機構と同 じである。詳細は、このページの後の方に記載する。
ネイティブメソッドには、整数、論理型値、フロートなどのプリミティブデータ型を引き渡すことができる。InputFile のread()
メソッドと OutputFile のwrite()
メソッドはどちらも整数の引数を受け取るようになってい る。最初の文は// in InputFile.java public native int read(byte b[], int len); // in OutputFile.java public native int write(byte b[], int len);read()
メソッドの 2 番目の引数が整数であると宣言している。同様に、2 番目の文もwrite()
メソッドの 2 番目の引数が整 数であると宣言している。1 番目の引数についてはこのページで後述するので、しば らくは考えなくてよい。整数のほかにも、フロート、論理型値、倍精度、文字、その他のプリミティブデータ型をネイティブメソッドに引き渡すことができる。ネイティブ言語側では、このよう なプリミティブデータ型はネイティブ言語の最も一致度の高いデータ型に割り当てら れる。たとえば、ここの例の C 言語側では、
InputFile_read()
関数のシグニチャは次のようになる。ここでは真ん中の引数のことは無視してよい。後で説明する。// in InputFileImpl.c long InputFile_read(struct HInputFile *this, HArrayOfByte *buffer, long count)関数が
long
として整数引数を受け入れていることに注意する。これ は、C 言語のロング整数が Java の整数と最も近い型だからである。表に、Java の型と C 言語の型との対応を示す。C 言語で書かれたネイティブメソッドに正しいデータを渡すには、この表に示された対応づけに従う必要がある。同様に、
OutputFile_write()
関数は、Java のint
が 渡されたところをlong
で受け入れている。これでプリミティブデータ型をメソッドに引き渡したので、今度はそれを使ってみる ことにする。このようなプリミティブデータ型の引数は、他の C 言語変数とまったく同じように名前で使用することができる。たとえば、// in OutputFileImpl.c long OutputFile_write(struct HOutputFile *this, HArrayOfByte *buffer, long count)InputFile_read()
とOutputFile_write()
の両方に発生する次のコード片は、count
引数を使用して読み取りまたは書き込みのバイト数を設定している。if (len < count) { actual = len; } else { actual = count;}
プリミティブ型のパラメータだけでなく、リファレンスデータ型もネイティブメソッ ドへ引き渡すことができる。リファレンスデータ型には配列や、文字列、すべてが第 一クラスの Java オブジェクトであるオブジェクトなどがある。Java オブジェクトは C 言語のstruct
への ハンドル としてネイティブメソッドへ渡される。実際、Java 実行時システムはこの機構を利用して、呼び出されて いるネイティブメソッドが属する Java オブジェクトを渡す。この動作については、こ のページの自動パラメータ節ですでに紹介している。InputFile の
read()
メソッドはバイト配列を受け入れる。このメソッドは入力ファイルからバイトを読み取り、この配列を通してバイ トを返している。OutputFile のwrite()
メソッドもバイト配列を受 け入れ、この配列にはいっているバイトを出力ファイルに書き込むようになっている 。オブジェクト引数を受け取るネイティブメソッドの Java 側は簡単である。通常の Java メソッドと同じように、単にそのメソッドを宣言するだけである。
最初の文は// in InputFile.java public native int read(byte b[], int len); // in OutputFile.java public native int write(byte b[], int len);read()
メソッドへの最初の引数がバイト配列であること を宣言している。2 番目の文でもwrite()
メソッドへの最初の引数がバイト配列であることを宣言している。C 言語側では、
read()
とwrite()
メソッドを実装する 2 つの関数の宣言は次のようになる。どちらの関数でも 2 番目の引数は Java 側から渡されたバイト配列の引数である。 Java の配列は第一クラスのオブジェクトである。したがって、配列はオブジェクトのように、C 言語の// in InputFileImpl.c long InputFile_read(struct HInputFile *this, HArrayOfByte *buffer, long count) // in OutputFileImpl.c long OutputFile_write(struct HOutputFile *this, HArrayOfByte *buffer, long count)struct
へのハンドルとして渡される。構造体の 名前は大文字の 'H' とクラス名で構成される。 ただし、完全修飾されたクラス名が使用されていることに注意する。配列の場合の完 全修飾されたクラス名はArrayOf
に、配列内部に保持されている要素のデータ型を加えたものである。したがって、バイト配列の完全修飾されたクラス名 はArrayOfByte
であり、整数配列の完全修飾されたクラス名はArrayOfInt
である。他のクラスの完全修飾名には、そのクラスが宣言されているパッケージの名前が組み込まれる。したがって String は Hjava_lang_String 構造体へのハンドルとして、 Object は Hjava_lang_Object 構造体へのハンドルとして、Stack は Hjava_util_Stack 構造体へのハンドルとして、という具合に引き渡される。
これで Java オブジェクトに対する C 言語関数でのハンドルを得られたが、これで何ができるのだろうか ? このハンドルを使用すると、オブジェクトのメンバ変数にアクセスできるのである 。これについてはネイティブメソッドで Java オブジェクトを使用するで説明する。
ガベージコレクタがバックグラウンドで活動しているため、ネイティブメソッドへ渡 されたすべてのオブジェクトのリファレンスを慎重に維持し、オブジェクトがヒープ上の別の場所へ動かないようにする必要がある。リファレンスは C 言語スタックになければならない (広域変数であってはならない)。ただし、通常はあまり気を遣う必要はない。 ネイティブメソッドに渡される引数は、普通は特定オブジェクトへのリファレンスをガベージコレクタに知らせるのに十分な能力をもっているからである。しかしながら、広域変数を扱っている場合や正統でないポインタ操作 を行っている場合は、Java オブジェクトに対するネイティブメソッドでのリファレンスを常にガベージコレクタに通知するよう留意する必要がある。ガベージコレクタはオブジェクトの中間へのポインタをそのオブジェクトへのリファ レンスとして認識しないことに注意する。したがって、あるオブジェクトに対して維持しようとするリファレンスは、そのオブジェクトの先頭 をポイントしなければならない !
ネイティブメソッドを実装する |