EeBlog(テクニカルブログ)

第114回 ネイティブメソッドで Hello World

Java には native というキーワードがありますが Java の入門書のキーワード一覧で見たことがあるだけで使い道を知らないという人がほとんどではないでしょうか。
このキーワードは native修飾子といい、メソッドがネイティブメソッドであることを示します。 ネイティブメソッドとはメソッドがJavaではない他の言語で実装されているメソッドのことです。 他の言語でメソッドを実装することでOSのシステムコールやライブラリを呼び出すことができるようになりますが、 プラットフォームに依存した実装になるため Java の「Write Once, Run Anywhere」というスローガンは当てはまらなくなります。
では、実際にこの native修飾子を使って以下の Hello World プログラムをネイティブメソッドで書き直してみましょう。

hello/Hello.java


package hello;
public class Hello {
    public static void main(String[] args) {
        System.out.println("Hello, World!");
    }
}
>java hello.Hello
Hello, World!

ネイティブメソッド版の Hello World は以下のようになります。

hello/JNIHello.java


package hello;
public class JNIHello {
    static {
        System.loadLibrary("hello_JNIHello");
    }
    public static native void main(String[] args);
}

System.loadLibrary() メソッドは引数の名前のダイナミックライブラリをロードするメソッドです。 このメソッドはネイティブメソッドを呼び出す前に呼び出す必要があるので staticブロックに記述し、 クラスがクラスローダにロードされると同時にダイナミックライブラリもロードされるようにしています。今回ロードしているダイナミックライブラリ hello_JNIHello は後でC言語で実装します。
ネイティブメソッドの実装は他の言語で記述するためJavaのソース上では処理を記述しません。
では次にC言語でネイティブメソッドの処理を実装しましょう。 C言語でネイティブメソッドを実装するには JNI を使用します。 JNI とは C/C++ と Java がやりとりを行うための ABI と API の仕様です。
まずは Java のクラスファイルから C/C++ のヘッダファイルを生成します。 以下のコマンドを実行することでヘッダーファイル hello_JNIHello.h が生成されます。

javah -jni hello.JNIHello

生成されたヘッダファイルの内容は以下のようになります。

hello_JNIHello.h


/* DO NOT EDIT THIS FILE - it is machine generated */
#include 
/* Header for class hello_JNIHello */

#ifndef _Included_hello_JNIHello
#define _Included_hello_JNIHello
#ifdef __cplusplus
extern "C" {
#endif
/*
 * Class:     hello_JNIHello
 * Method:    main
 * Signature: ([Ljava/lang/String;)V
 */
JNIEXPORT void JNICALL Java_hello_JNIHello_main
  (JNIEnv *, jclass, jobjectArray);

#ifdef __cplusplus
}
#endif
#endif

C/C++ の Java_hello_JNIHello_main() 関数が Java の hello.JNIHello.main() ネイティブメソッドと対応します。
C言語で実装した Hello World を以下に示します。

hello_JNIHello.c


#include "hello_JNIHello.h"
#include 

JNIEXPORT void JNICALL
Java_hello_JNIHello_main(JNIEnv * env, jclass cls, jobjectArray args)
{
  printf("Hello, World!\n");
}

生成したヘッダファイルをインクルードし、ネイティブメソッドに対応する関数で Hello World を実装しています。
後は hello_JNIHello.c をコンパイル及びリンクし、ダイナミックライブラリを作成します。
ネイティブメソッド版の実行結果は以下のようになります。

実行結果

>java -Djava.library.path=. hello.JNIHello
Hello, World!

-Djava.library.path=. は System.loadLibrary() メソッドでロードされるダイナミックライブラリが存在するディレクトリを指定しています。

以上、ネイティブメソッドで Hello World でした。