Previous | Next | Trail Map | Integrating Native Methods into Java
Programs | ネイティブメソッドを実装する


ネイティブメソッドにデータを引き渡す

前のページで説明したように、Java 実行時、システムは引数リストの最初の引数としてネイティブメソッドに特別な引数を渡す。これについてもう少し詳しく説明した後 、ユーザ独自の引数をネイティブメソッドに渡す方法を解説する。

自動パラメータ

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 で受け入れている。

    // in OutputFileImpl.c
long OutputFile_write(struct HOutputFile *this,
                      HArrayOfByte *buffer,
                      long count)

これでプリミティブデータ型をメソッドに引き渡したので、今度はそれを使ってみる ことにする。このようなプリミティブデータ型の引数は、他の C 言語変数とまったく同じように名前で使用することができる。たとえば、InputFile_read()OutputFile_write() の両方に発生する次のコード片は、count 引数を使用して読み取りまたは書き込みのバイト数を設定している。

if (len < count) {
    actual = len;
} else {
    actual = count;}

プリミティブデータ型の引数はネイティブ関数に対して値で 渡されること に注意する。つまり、ネイティブ関数の内部で引数のどれかを変更しても、その変更 が呼び出し元の Java メソッドには影響しないということである。ネイティブメソッ ドの引数を通して 1 つまたは複数の値を返すときは、オブジェクトを使用する。

リファレンスデータ型の引き渡し

プリミティブ型のパラメータだけでなく、リファレンスデータ型もネイティブメソッ ドへ引き渡すことができる。リファレンスデータ型には配列や、文字列、すべてが第 一クラスの 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 つの関数の宣言は次のようになる。

    // 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)

どちらの関数でも 2 番目の引数は Java 側から渡されたバイト配列の引数である。 Java の配列は第一クラスのオブジェクトである。したがって、配列はオブジェクトのように、C 言語の struct へのハンドルとして渡される。構造体の 名前は大文字の 'H' とクラス名で構成される。 ただし、完全修飾されたクラス名が使用されていることに注意する。配列の場合の完 全修飾されたクラス名は ArrayOf に、配列内部に保持されている要素のデータ型を加えたものである。したがって、バイト配列の完全修飾されたクラス名 は ArrayOfByte であり、整数配列の完全修飾されたクラス名はArrayOfInt である。

他のクラスの完全修飾名には、そのクラスが宣言されているパッケージの名前が組み込まれる。したがって String は Hjava_lang_String 構造体へのハンドルとして、 Object は Hjava_lang_Object 構造体へのハンドルとして、Stack は Hjava_util_Stack 構造体へのハンドルとして、という具合に引き渡される。

これで Java オブジェクトに対する C 言語関数でのハンドルを得られたが、これで何ができるのだろうか ? このハンドルを使用すると、オブジェクトのメンバ変数にアクセスできるのである 。これについてはネイティブメソッドで Java オブジェクトを使用するで説明する。

警告

ガベージコレクタがバックグラウンドで活動しているため、ネイティブメソッドへ渡 されたすべてのオブジェクトのリファレンスを慎重に維持し、オブジェクトがヒープ上の別の場所へ動かないようにする必要がある。リファレンスは C 言語スタックになければならない (広域変数であってはならない)。ただし、通常はあまり気を遣う必要はない。 ネイティブメソッドに渡される引数は、普通は特定オブジェクトへのリファレンスをガベージコレクタに知らせるのに十分な能力をもっているからである。しかしながら、広域変数を扱っている場合や正統でないポインタ操作 を行っている場合は、Java オブジェクトに対するネイティブメソッドでのリファレンスを常にガベージコレクタに通知するよう留意する必要がある。

ガベージコレクタはオブジェクトの中間へのポインタをそのオブジェクトへのリファ レンスとして認識しないことに注意する。したがって、あるオブジェクトに対して維持しようとするリファレンスは、そのオブジェクトの先頭 をポイントしなければならない !


Previous | Next | Trail Map | Integrating Native Methods into Java
Programs | ネイティブメソッドを実装する