Skip to content

Declaring Java Types

Eugene Gershnik edited this page Apr 19, 2023 · 5 revisions

Raw JNI provides very little in the way of type safety. There are jobject, jstring and a few other predefined types but most of the time you are on your own. Pretty much everything is jobject and if you mix up OneJavaType with AnotherJavaType because of that - prepare to deal with runtime crash.

SimpleJNI rectifies this by requiring you to declare all Java types being used, either in JNI methods you implement or in Java methods/fields you access. Fortunately this is a very simple process. You declare a non-array (a.k.a.) Java type like this

DEFINE_JAVA_TYPE(jSomething,       "com.mystuff.Something");

The first argument is the name of the C++ pointer type to create to represent the Java class. The second is the fully qualified name of the Java class. How to name the C++ type is up to you. In this guide we will follow jJavaSimpleName convention. Note that jSomething is a pointer type, just like jobject is. The declaration above should be put in the global namespace and the C++ type is created in global namespace. In the future we might expose a way to declare it in a namespace of your choosing.

Nested classes

The fully qualified name of a nested Java class is simply com.mystuff.Something$Nested. That is, use $ sign to separate nested types from their owners.

Arrays

Arrays are first class objects in Java so to use arrays you need to also declare a C++ representation. Don't forget to declare the element type first if you haven't done so!

DEFINE_JAVA_TYPE(jSomething,       "com.mystuff.Something");
DEFINE_ARRAY_JAVA_TYPE(jSomething); //Declares jSomethingArray 

The second line in the code above declares a jSomethingArray type. The Array suffix mimics the built-in JNI's jintArray etc. and currently cannot be changed.

Conversions

Java knows that a HashMap instance can be cast to Map but if we simply declare

DEFINE_JAVA_TYPE(jHashMap,    "java.util.HashMap");
DEFINE_JAVA_TYPE(jMap,        "java.util.Map"); 

the C++ side has no idea that a jHashMap can be converted to jMap. All such types automatically convert to jobject (the 'pointed to' type inherits from the 'posted to' type of jobject). However, due to a the way JNI works it is impossible to use inheritance to do the same for arbitrary types. Instead you tell SimpleJNI that two Java types are convertible explicitly

DEFINE_JAVA_CONVERSION(jHashMap, jMap);

The order of arguments isn't important. It simply allows conversion both ways using jstatic_cast(note the j!) method or automatically for smart pointers.

jMap jm = ...;
jHashMap jhm = jstatic_cast<jHashMap>(jm);

local_java_ref<jMap> lrm = ...;
local_java_ref<jHashMap> lrhm = lrm; //succeeds with no casts

The syntax of jstatic_cast is meant to closely resemble that of static_cast of course. In practice you will fin that most of the time you will work with smart pointers so manual casts are unnecessary.