8

Is it possible to call a native CPP function using JNI which takes generic arguments? Something like the following:

public static native <T, U, V> T foo(U u, V v);

And then call it like:

//class Foo, class Bar, class Baz are already defined;
Foo f = foo(new Bar(), new Baz());

Can anyone please provide me with a sample which is actually doing this or some tutorial on the net which does this? I am asking because in my CPP JNI function (called by JVM), I get unsatisfied link error.

CPP Code follows:

JNIEXPORT jobject JNICALL Java_Processor_process (JNIEnv *env, jclass processor_class, jobject obj1, jobject obj2)
{
    jclass bar_class = env->FindClass("Bar");
    jmethodID getFooMethod = env->GetMethodID(bar_class, "getFoo", "()Ljava/lang/Object;");
//getFoo() is defined as `public Foo getFoo();` in Bar.java
    return env->CallObjectMethod(obj1, getFooMethod);
}

EDIT:

I have tried by modifying the code but now I am getting NoSuchMethodError:

Java code:

public static native <U, V> String foo(U u, V v);
//...
String str = foo(new Bar(), new Baz());

CPP code:

JNIEXPORT jstring JNICALL Java_Processor_process (JNIEnv *env, jclass processor_class, jobject obj1, jobject obj2)
{
    jclass bar_class = env->FindClass("Bar");
    jmethodID getFooMethod = env->GetMethodID(bar_class, "getFoo", "()Ljava/lang/String;");
    //getFoo() is now defined as `public String getFoo();` in Bar.java
    return env->CallObjectMethod(obj1, getFooMethod);
}

Does this mean that JNI has no support for generics or am I missing something?

Aaron S
  • 5,023
  • 4
  • 29
  • 30

2 Answers2

12

In general you should always use javap -s to get the signatures of methods you are going to be looking for in JNI. Don't guess.

user207421
  • 305,947
  • 44
  • 307
  • 483
  • 3
    I disagree. My native library has 51 functions and I hand-coded them all with no problems. It's really not at all difficult to get them right if you just stop and think for a minute about what you are doing. – Lawrence Dol Mar 11 '10 at 07:06
  • 8
    But why run *any* risk, when you have a tool that is already 100% correct? – user207421 Apr 06 '10 at 10:42
  • 2
    `javah` will take a class file and output a C header file containing the exact functions you need to implement – Michael Mrozek Aug 12 '10 at 22:16
  • 3
    What we are discussing here is signatures of *Java* methods, to be called *from* JNI. – user207421 Aug 13 '10 at 09:10
  • 2
    There's no risk. In both cases you need to have tests covering the things that were built. Either the tests pass or they don't. Hand or machine generated is irrelevant. – James Moore Jul 16 '12 at 16:23
  • 3
    @JamesMoore There is nothing irrelevant about it. You don't do things that the computer can do for you better and faster and more accurately. It is literally a waste of time. It introduces major cost and error risks with a high probability of occurring and that have a 100% effective mitigation strategy: don't do it. – user207421 Sep 27 '12 at 17:38
9

There are numerous questions regarding type erasure on stack overflow (e.g. Get generic type of java.util.List), what you're looking to do is neither possible with JNI nor Java itself. The runtime type signature of foo is (in both worlds, or actually, there is only one world) Object foo(Object u, Object v), which will do an implicit class cast on the return value to whatever type you call it with.

As you might notice (and as mentioned in the comment to your question), there's no way for you to know what type T is.

EDIT:
By the way, the getFoo method is supposed to return 'Foo', so shouldn't you be doing

jmethodID getFooMethod = env->GetMethodID(bar_class, "getFoo", "()LFoo;");

Come to think of it, your entire call sequence seems out of place... you've got a foo which is native, returning string. Now foo looks for getFoo in Bar, which returns 'Foo', and returns the result of that call directly, effectively trying to return a Foo (Foo getFoo() according to the comment) where string is expected.

Community
  • 1
  • 1
falstro
  • 34,597
  • 9
  • 72
  • 86