为什么有些Java库方法委托给具有几乎相同签名的本机方法?

Ram*_*hef 62 java native

在深入研究JRE库的源代码之后,我注意到一个奇怪的常见代码结构,如下所示:

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

private native int foo0(double bar);
Run Code Online (Sandbox Code Playgroud)

这个代码模式的目的是什么,为什么使用它而不是简单地将底层本机方法公开为公共方法?

Oli*_*myn 47

本机版本只是一个实现细节.

此模式将方法的公共接口与实际实现分开.

我看到至少5个有用的原因:

  • 测试目的(你可以模拟java方法调用)
  • 替代实现:特定版本的java库可以在纯java形式中实现该方法,而无需调用本机实现(在swing代码中常见).
  • 向后兼容性:如果在新版本中,该方法接受一些额外的参数,则可以保留原始的java方法签名并调整本机实现.
  • 输入/输出验证:大多数情况下,在调用本机版本之前,java代码将对输入参数进行一些检查,并在需要时抛出异常.
  • 隔离:直接使用本机代码的方法数量有限,允许对内部代码结构进行更多更改.

您可能会发现更多优势.

  • 事实上,根据http://stackoverflow.com/questions/10940502/is-it-possible-to-override-a-native-method-in-a-java-class-in-android-dalvik,似乎是原生的关键字不是方法签名的一部分,所以这个答案实际上没有意义. (22认同)
  • 我没有看到额外的间接层如何帮助解决这些问题.1)我不认为`native`螺丝嘲笑.2)不同的Java实现可以自由地使用不同的方法`native`而不会相互冲突.3)如果该方法在新版本中接受额外的参数(不太可能;这会破坏反向兼容性),所需的更改大致相同.4)我不知道这是否真的是真的,那么非"native"方法直接委托给`native`的呢?... (16认同)
  • @OlivierSamyn:规范*不指定方法是否为"native".`native`也可能是函数体的一部分,因为它对外部用户的所有可见性.当`native`方法是私有的并且由用Java编写的公共方法包装时,`native`就像隐藏一样. (5认同)
  • @OlivierSamyn没有"文档观点",因为`native`不是文档签名的一部分.javadoc忽略了`native`关键字.来自http://docs.oracle.com/javase/7/docs/technotes/guides/javadoc/whatsnew-1.2.html:_从签名中删除"synchronized"和"native".Javadoc生成API规范.这两个关键字不属于规范的签名,因为它们是特定于实现的.关键字"native"不需要记录._ (3认同)
  • `native`不是界面的一部分. (2认同)
  • 即使从编译器的角度来看,native关键字不是接口的一部分,如果你在公共API(javadoc)中添加它,其他实现必须使它本机完全兼容(从文档的角度来看).为了向后兼容,将参数添加到C函数并处理java版本中的默认值更简单. (2认同)
  • @OlivierSamyn,其他实现必须具有完全相同的javadoc注释才能完全兼容吗?我不这么认为.仅仅因为它在文档中并不意味着你需要兼容性. (2认同)
  • @OlivierSamyn:它甚至不是Javadoc的一部分.例如,`String.intern`在参考实现中是原生的,但在文档中没有提到`native`. (2认同)

Win*_*ert 14

private native int foo(double bar);
Run Code Online (Sandbox Code Playgroud)

因此,最终必须为其实现调用C++函数.特别是,它最终会调用一个名称类似的函数:

Java_MyClass_foo
Run Code Online (Sandbox Code Playgroud)

如果有多个具有不同签名的本机foo方法会发生什么?并发症.如果这样做,Java会将类型信息添加到它查找的方法的名称中.但是如果你坚持使用非重载方法,那就简单多了.

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

private native int foo0(double bar);
Run Code Online (Sandbox Code Playgroud)

foo0已被赋予一个独特的名称,永远不应该有理由添加另一个foo0.这使得C++的实现变得简单,它永远不必处理受损的名称.即使foo最终获得了重载,它也会调用foo1而不是foo0C++ JNI实现将不必处理重载的额外复杂性.

  • 这可能是一个原因,但是**非常不可能.实际上,没有并发症.特别是,没有重载**:`javah`头生成器将检测歧义/重载方法,并做一些[名称修改](https://en.wikipedia.org/wiki/Name_mangling)来制作签名独特.(事实上​​,它必须是因为JNI是普通的C而不是C++). (2认同)