Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

JNI - Converting jobject representing Basic Java Objects (Boolean) to native basic types (bool)

I think I managed to fit most of the question in to the title on this one!

I'm pulling back an Object from Java in my native C++ code:

jobject valueObject = env->CallObjectMethod(hashMapObject, hashMapGetMID, keyObject);

It's possible for me to check wether the return object is one of the native types using something like:

jclass boolClass = env->FindClass("java/lang/Boolean");
if(env->IsInstanceOf(valueObject, boolClass) == JNI_TRUE) { }

So, I now have a jobject which I know is a Boolean (note the upper case B) - The question is, what is the most efficient way (considering I already have the jobject in my native code) to convert this to a bool. Typecasting doesn't work which makes sense.

Although the above example is a Boolean I also want to convert Character->char, Short->short, Integer->int, Float->float, Double->double.

(Once i've implemented it I will post an answer to this which does Boolean.booleanValue())

like image 522
Graeme Avatar asked Jun 23 '11 09:06

Graeme


2 Answers

You have two choices.

Option #1 is what you wrote in your self-answer: use the public method defined for each class to extract the primitive value.

Option #2 is faster but not strictly legal: access the internal field directly. For Boolean, that would be Boolean.value. For each primitive box class you have a fieldID for the "value" field, and you just read the field directly. (JNI cheerfully ignores the fact that it's declared private. You can also write to "final" fields and do other stuff that falls into the "really bad idea" category.)

The name of the "value" field is unlikely to change since that would break serialization. So officially this is not recommended, but in practice you can get away with it if you need to.

Either way, you should be caching the jmethodID / jfieldID values, not looking them up every time (the lookups are relatively expensive).

You could also use the less expensive IsSameObject function rather than IsInstanceof, because the box classes are "final". That requires making an extra GetObjectClass call to get valueObject's class, but you only have to do that once before your various comparisons.

BTW, be careful with your use of "char". In your example above you're casting the result of CallCharMethod (a 16-bit UTF-16 value) to a char (an 8-bit value). Remember, char != jchar (unless you're somehow configured for wide chars), long != jlong (unless you're compiling with 64-bit longs).

like image 145
fadden Avatar answered Oct 21 '22 12:10

fadden


This is the solution I'm going to use if I get no more input. Hopefully it isn't this difficult but knowing JNI i'm thinking it might be:

    if     (env->IsInstanceOf(valueObject, boolClass)           == JNI_TRUE)
    {
        jmethodID booleanValueMID   = env->GetMethodID(boolClass, "booleanValue", "()Z");
        bool booleanValue           = (bool) env->CallBooleanMethod(valueObject, booleanValueMID);
        addBoolean(key, booleanValue);
    }
    else if(env->IsInstanceOf(valueObject, charClass)           == JNI_TRUE)
    {
        jmethodID characterValueMID  = env->GetMethodID(charClass, "charValue", "()C");
        char characterValue          = (char) env->CallCharMethod(valueObject, characterValueMID);
        addChar   (key, characterValue);
    }
like image 25
Graeme Avatar answered Oct 21 '22 13:10

Graeme