Java 5.0から導入されたJavaのenumですが、JNIを経由したnativeメソッド内ではどのように扱えばよいのでしょうか?これについては、JNI仕様にも明記されていないようです。必要があったので、ちょっと調べてみました。
まず、次のようなテストコードを書いて、
package test; public class ClassWithEnum { enum Result { OK, NG } public native void method(Result result); }
javah -jni でヘッダを出力してみます。
/* DO NOT EDIT THIS FILE - it is machine generated */ #include <jni.h> /* Header for class test_ClassWithEnum */ #ifndef _Included_test_ClassWithEnum #define _Included_test_ClassWithEnum #ifdef __cplusplus extern "C" { #endif /* * Class: test_ClassWithEnum * Method: method * Signature: (Ltest/ClassWithEnum/Result;)V */ JNIEXPORT void JNICALL Java_test_ClassWithEnum_method (JNIEnv *, jobject, jobject); #ifdef __cplusplus } #endif #endif
enumのResultは、nativeメソッドにjobject型で渡されることがわかります。では、このjobjectからenum値を取り出すには、どうすればよいのでしょうか?
実は、Javaのenumは、コンパイル時に暗黙でjava.lang.Enum型を継承したクラスに変換されています。確認のため、javapでResultクラスをデコンパイルすると、次のような結果が得られます。
Compiled from "ClassWithEnum.java"
final class test.ClassWithEnum$Result extends java.lang.Enum{
public static final test.ClassWithEnum$Result OK;
public static final test.ClassWithEnum$Result NG;
static {};
public static test.ClassWithEnum$Result[] values();
public static test.ClassWithEnum$Result valueOf(java.lang.String);
}
ここまで判れば、後は普通のjobjectを扱うのと同様です。引数のResultがOKかどうかを調べる処理は、
void JNICALL Java_test_ClassWithEnum_method(JNIEnv *env, jobject obj, jobject result) { jclass result_class = env->FindClass("Ltest/ClassWithEnum$Result;"); jfieldID result_ok_field = env->GetStaticFieldID(result_class, "OK", "Ltest/ClassWithEnum$Result;"); jobject result_ok_object = env->GetStaticObjectField(result_class, result_ok_field); jmethodID equals_method = env->GetMethodID(result_class, "equals", "(Ljava/lang/Object;)Z"); if (env->CallBooleanMethod(result_ok_object, equals_method, result) == JNI_TRUE) { // Result OK } }
と書くことができます(エラー処理省略)。同一のclass staticフィールドを参照していても、jobject同士の”==”比較ではTRUEにならないようなので、equalsメソッドを使っています。
(2009.2.14 追記)
これらのjobjectは、”同一のJava objectへの参照”を持つ別のインスタンスなので、単純な”==”比較はできません。こういった場合は、わざわざequalsメソッドを取得しなくても、JNI関数のIsSameObject()が使えます。
戻り値としてResultの値を返すなら、GetStaticObjectFieldで取得したjobjectを返せばOKです。enum値をStringに変換するname()、Stringをenum値に変換するvalueOf()などのメソッドも使えます。
JNIとしては、enumだからといって特別な処理を行っているわけではなく、通常のjobject型の扱いと同じなので、JNIの仕様には明記していないということなのでしょう。
enumの扱い、一生懸命JNIの仕様探していました。助かりました・・・ありがとうございます。
コメントありがとうございます。私もいろいろ探したのですが、見つからなかったので調べてみました。お役に立ててなによりです。