Обычно это будет зависеть от вашей среды. Я использовал только KNI, который еще более примитивен, чем JNI. Я думаю, что немало уродства неизбежно, поскольку вы смешиваете отслеживание памяти в двух системах, только в одной из которых есть GC.
В общем, я нашел, что лучше всего обернуть все вызовы кода C в функции, которые позаботились о грязном приведении, которое, я думаю, неизбежно. (Кстати, я буду использовать C для обозначения не Java-кода здесь)
На стороне C перемещение объектов Java, безусловно, является потенциальной проблемой. Это будет зависеть от вашей платформы, но я ожидаю, что, пока вы находитесь в lib, вы не можете ожидать появления Java GC, поэтому ваши объекты стабильны. ВЫ ДОЛЖНЫ БЫТЬ УВЕРЕНЫ В ЭТОМ. С другой стороны, если это не так, вы в значительной степени облажались. Предполагая, что это так, вы хотите сделать то же самое, изолировав разыменование / приведение к функции, которая работает с JNI, чтобы вы могли счастливо работать с обычными объектами C во всех вызываемых функциях.
Что может быть действительно уродливым, так это то, что вы можете позволить объектам выходить из области видимости с обеих сторон, поскольку потенциально любая сторона может удерживать ссылку на ваш объект. Здесь мы использовали финализаторы на стороне Java, а также деструкторы на стороне C. Это было не красиво, но я думаю, что это несколько неизбежно.
Итак, короткий ответ, это будет несколько уродливо, изолировать уродство вокруг интерфейса между двумя языками, так что для большей части работы на любом языке вам не придется беспокоиться о таких вещах.
Также стоит иметь базовый класс для объектов, существующих в этом интерфейсе, так как здесь вы также можете выделить некоторые уродства.