Почему некоторые методы библиотеки Java делегируют собственный метод с почти идентичной подписью?

После углубления в исходный код библиотеки JRE я заметил необычно распространенную структуру кода, например:

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

private native int foo0(double bar);

В чем заключается цель этого шаблона кода и почему он используется вместо простого раскрытия основного метода native как общедоступного?

Ответ 1

Нативная версия - это просто деталь реализации.

Этот шаблон отделяет открытый интерфейс метода от фактической реализации.

Я вижу по крайней мере 5 причин, почему это полезно:

  • Цель теста (вы можете высмеять вызов метода java)
  • альтернативная реализация: конкретная версия java-библиотеки может реализовать этот метод в чистой форме java без вызова собственной реализации (общая в swing-коде).
  • обратная совместимость: если в новой версии метод принимает некоторые дополнительные параметры, исходная сигнатура метода java может быть сохранена и адаптирована адаптивная адаптация.
  • проверка ввода-вывода: большую часть времени, прежде чем вызывать собственную версию, код Java будет выполнять некоторые проверки входных параметров и, если необходимо, генерировать исключение.
  • Изоляция: количество методов, непосредственно использующих собственный код, ограничено, что позволяет больше изменений во внутренней структуре кода.

Вероятно, вы можете найти больше преимуществ.

Ответ 2

private native int foo(double bar);

Итак, в конце концов, это должно вызвать функцию С++ для ее реализации. В частности, это вызовет функцию с именем вроде:

Java_MyClass_foo

Что произойдет, если есть несколько собственных методов foo с разными сигнатурами? Осложнения. Если вы это сделаете, Java добавит информацию о типе в имя метода, который он ищет. Но это проще, если вы придерживаетесь неперегруженных методов.

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

private native int foo0(double bar);

foo0 было присвоено уникальное имя, никогда не должно быть причин добавить еще один foo0. Это сохраняет реализацию С++ простой, ей никогда не приходится иметь дело с искаженными именами. Даже если foo в конечном итоге получит перегрузку, он вызовет foo1 вместо foo0, а реализация С++ JNI не будет иметь дело с дополнительными осложнениями перегрузки.