Why do some Java library methods delegate to a native method with a nearly-identical signature?

JavaNative

Java Problem Overview


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?

Java Solutions


Solution 1 - Java

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.

Solution 2 - Java

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.

Attributions

All content for this solution is sourced from the original question on Stackoverflow.

The content on this page is licensed under the Attribution-ShareAlike 4.0 International (CC BY-SA 4.0) license.

Content TypeOriginal AuthorOriginal Content on Stackoverflow
QuestionRamenChefView Question on Stackoverflow
Solution 1 - JavaOlivier SamynView Answer on Stackoverflow
Solution 2 - JavaWinston EwertView Answer on Stackoverflow