La Java Native Interface o JNI è un framework del linguaggio Java che consente al codice Java di richiamare (o essere richiamato da) codice cosiddetto "nativo", ovvero specifico di un determinato sistema operativo o, più in generale, scritto in altri linguaggi di programmazione, in particolare C, C++ e assembly.[1] Nella letteratura in lingua italiana, il nome del framework viene in genere tradotto come "interfaccia nativa Java".[2]
La principale applicazione della JNI è quella di richiamare all'interno di programmi Java porzioni di codice che svolgono funzionalità intrinsecamente non portabili (per esempio primitive di sistema operativo) e che pertanto non possono essere implementate in Java puro.[3] L'interfacciamento è basato sulla definizione di un insieme di classi di raccordo fra i due contesti, che presentano una interfaccia Java, ma che delegano al codice nativo l'implementazione dei loro metodi.[3]
Una classe può definire un numero arbitrario di metodi implementati in codice nativo. Per far questo, nel sorgente della classe il metodo deve avere la parola-chiave native
e deve avere un punto e virgola al posto del corpo del metodo.[3] Ad esempio:
public class Classe {
public void metodo() { /* ... */ }
public native void metodoNativo();
}
Come si evince dall'esempio, non è necessario che la classe abbia solo metodi nativi.
Un metodo native
può essere static
e anche final
; non ha senso, invece, definire un metodo nativo come abstract
.
In genere, i metodi nativi vengono mantenuti privati dalla classe che li definisce, mentre dei metodi pubblici o protected
(invocati dai client o dalle sottoclassi) fungono da wrapper. In accordo con il principio dell'incapsulamento delle informazioni, questo consente di definire un'interfaccia per la classe che sia completamente indipendente dall'uso del codice nativo; in questo modo, sarà più facile in futuro modificare il comportamento della classe mantenendo un'interfaccia nativa retrocompatibile con le librerie native già implementate.
Il JNI viene principalmente utilizzato per consentire al programma l'accesso a funzioni definite nelle librerie del sistema operativo ospite mediante primitive di sistema. In realtà, l'accesso avviene in modo indiretto, nel senso che le funzioni del sistema operativo vengono invocate dalle funzioni che implementano i metodi nativi della classe che li definisce e che vengono a loro volta utilizzati dal codice Java.
L'uso del JNI si rende inoltre necessario quando l'implementazione di una certa funzionalità nel programma dipende dal sistema operativo in uso a run-time e non è presente nelle librerie standard di Java. Il programma risultante non può essere definito "100%-Java", in quanto esso fa direttamente uso di codice nativo.
Nell'implementazione Sun delle librerie standard della piattaforma Java, sono molti i metodi native. L'implementazione di questi metodi è presente solo nella macchina virtuale che verrà utilizzata a run-time.
L'utilizzo del JNI limita la portabilità del programma all'insieme delle piattaforme per le quali è realmente presente un'implementazione della libreria nativa. Questo significa che la classe che fa uso di metodi nativi non potrà essere utilizzata su tutti i sistemi per i quali è presente un Java Runtime Environment. Di fatto, un programma che faccia uso di classi con metodi nativi non può essere definito "100% Java".
Per questo motivo, è consigliabile utilizzare JNI solo se strettamente necessario o se il programma verrà utilizzato unicamente su un numero limitato di piattaforme specifiche.
Le librerie standard sono un'eccezione: l'implementazione dei metodi nativi da esse definiti è (necessariamente) presente in ogni virtual machine e spesso è addirittura necessaria per la corretta esecuzione del programma stesso.