62

After delving into the source code for the JRE library, I noticed a strangely common code structure, like this:

public int foo(double bar) {
    return foo0(bar);
}

private native int foo0(double bar);

What is the purpose of this code pattern, and why is it used instead of simply exposing the underlying native method as the public one?

RamenChef
  • 5,557
  • 11
  • 31
  • 43
  • 10
    Good question. You'd have to ask the language designers. Maybe when some of the early libraries were designed, they hadn't yet decided whether or not it would be possible to override a native method. Just guessing. – Dawood ibn Kareem Aug 15 '16 at 01:16
  • 2
    The defination of "many" is subjective. – Raedwald Aug 15 '16 at 06:53
  • 14
    As someone who wrote lots of JNI code, I have to say that I'm using the same pattern consequently - last but not least, it's nice to quickly add some `System.out.println("The method is called, so why the heck doesn't it work!?!?!");` in the Java part, without having to recompile any native code. – Marco13 Aug 15 '16 at 23:46

2 Answers2

47

The native version is just an implementation detail.

This pattern separates the public interface of the method from the actual implementation.

I see at least 5 reasons why this is useful:

  • test purpose (you can mock the java method call)
  • alternative implementation: a particular version of the java library may implement that method in pure java form without call to the native implementation (common in swing code).
  • backward compatibility: if in a new version, the method accepts some extra parameters, the original java method signature can be kept and the native implementation adapted.
  • input/output validation: most of the time, before calling the native version, the java code will do some checks on the input parameters and throw exception if needed.
  • isolation: the number of methods that directly uses the native code is limited, allowing more changes in the internal code structure.

You can probably find more advantages.

Olivier Samyn
  • 1,105
  • 8
  • 13
  • 16
    I don't see how the extra layer of indirection helps with any of this. 1) I don't think `native` screws with mocking. 2) Different Java implementations are free to make different methods `native` without conflicting with each other. 3) If the method accepts extra parameters in a new version (unlikely; that'd break backward compatilibity), the changes required are about the same either way. 4) I don't know whether that's actually true, and what about the ones where the non-`native` method just delegates to the `native` one directly? ... – user2357112 Aug 15 '16 at 04:02
  • 5) Limiting the number of methods that directly call the `native` method doesn't really permit more changes to the code's structure. – user2357112 Aug 15 '16 at 04:02
  • 22
    Indeed, as per http://stackoverflow.com/questions/10940502/is-it-possible-to-override-a-native-method-in-a-java-class-in-android-dalvik it seems that the native keyword isn't part of the method signature, so this answer doesn't actually make sense. – Winston Ewert Aug 15 '16 at 04:37
  • 2
    `native` is not part of the interface. – user253751 Aug 15 '16 at 05:57
  • 2
    Even if the native keyword is not part of the interface from a compiler point of view, if you add it in the public API (javadoc), other implementation have to make it native to be fully compatible (from a documentation point of view). For backward compatibility, it's more simple to add a parameter to the C function and deal with the default values in the java version. – Olivier Samyn Aug 15 '16 at 09:37
  • The java bytecode emulator is easy for programmers to use, but it would not be performant if it were built as hardware. There comes a point where a native method outperforms the Java bytecode emulator sufficiently to be worth moving to a native implementation. This pattern allows the native implementation to be invisible to the java library – pojo-guy Aug 15 '16 at 13:39
  • 2
    @OlivierSamyn, do other implementations have to have the exact same javadoc comment in order to be fully compatible? I don't think so. Just because its in the documentation doesn't mean you need it for compatibility. – Winston Ewert Aug 15 '16 at 15:10
  • 2
    @OlivierSamyn: It's not even part of the Javadoc. For example, `String.intern` is native in the reference implementation, but there's no mention of `native` in the documentation. – user2357112 Aug 15 '16 at 15:57
  • If the spec specifies "native" in the method signature, I expect it to be implemented in that way. Even if the result of a method call is the same. – Olivier Samyn Aug 15 '16 at 16:52
  • @user2357112 that's exactly the point: public documentation and method signatures are not native. It's an implementation detail, and as such is hidden in private methods. – Olivier Samyn Aug 15 '16 at 16:56
  • 5
    @OlivierSamyn: The spec *does not* specify whether methods are `native`. `native` might as well be part of the function body for all its visibility to outside users. `native` is just as hidden when the public method is `native` directly as it is when the `native` method is private and wrapped by public methods written in Java. – user2357112 Aug 15 '16 at 17:02
  • 3
    @OlivierSamyn There is no "documentation point of view" because `native` is not part of the documented signature. javadoc ignores the `native` keyword. From http://docs.oracle.com/javase/7/docs/technotes/guides/javadoc/whatsnew-1.2.html: _Remove "synchronized" and "native" from signatures. Javadoc generates an API specification. These two keywords do not belong in the signatures of a specification, because they are implementation-specific. The keyword "native" does not need to be documented._ – jamesdlin Aug 16 '16 at 06:43
  • Ok, I have not done extended research before answering that question. I just tried to explain why the code is written as it is, not if it's a good or bad idea, or if it could have been done in another way or what else. I did a lot of jni code in the past and I always encapsulated the C calls in private functions, for the reasons I gave just above. – Olivier Samyn Aug 16 '16 at 08:07
  • @WinstonEwert maybe it isn't but you can use reflection http://stackoverflow.com/questions/11869598/using-java-reflection-can-you-detect-if-a-method-is-native-or-not to check if method is native. Then on different JVM's you may get different results for `isNative()` invocation on that method and since it is part of public API this should be avoided. – csharpfolk Aug 21 '16 at 06:20
  • 1
    @csharpfolk, since reflection also lets you inspect the private parts of classes, I don't think that reasoning works. – Winston Ewert Aug 22 '16 at 21:57
14
private native int foo(double bar);

So, eventually this has to call a C++ function for its implementation. In particular, it'll end up calling a function with a name something like:

Java_MyClass_foo

What happens if there are multiple native foo methods with different signatures? Complications. If you do it, Java will add type information into the name of the method it looks for. But it is simply easier if you stick to non-overloaded methods.

public int foo(double bar) {
    return foo0(bar);
}

private native int foo0(double bar);

foo0 has been given a unique name, there should never be a reason to add another foo0. This keeps the implementation of the C++ simple, it never has to deal with mangled names. Even if foo eventually gains an overload, it will call foo1 instead of foo0 and the C++ JNI implementation won't have to deal with the extra complications of overloading.

Winston Ewert
  • 44,070
  • 10
  • 68
  • 83
  • 2
    That may be a reason, but a **very** unlikely one. Practically, there are no complications. Particularly, there will be **no overloading**: The `javah` header generator will detect ambiguities/overloaded methods, and do some [name mangling](https://en.wikipedia.org/wiki/Name_mangling) to make the signatures unique. (It has to, in fact, because JNI is plain C, and not C++). – Marco13 Aug 15 '16 at 22:06
  • @Marco13, I explicitly noted that in my answer, "Java will add type information into the name of the method it looks for" and talked about mangled names. – Winston Ewert Aug 15 '16 at 22:40