JNIでenumを扱う

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の仕様には明記していないということなのでしょう。

2 Comments

  1. enumの扱い、一生懸命JNIの仕様探していました。助かりました・・・ありがとうございます。

  2. コメントありがとうございます。私もいろいろ探したのですが、見つからなかったので調べてみました。お役に立ててなによりです。

コメントを残す

メールアドレスが公開されることはありません。 * が付いている欄は必須項目です

*