Qual è il punto di invokeinterface?

Sto leggendo questo articolo su come JVM invoca metodi, e penso di averne ottenuto la maggior parte. Tuttavia, sto ancora avendo problemi a capire la necessità di invokeinterface .

Per come la capisco, una class ha fondamentalmente una tabella virtuale di metodi e quando si chiama un metodo con invokevirtual o invokeinterface viene consultata questa tabella virtuale.

Qual è la differenza, quindi, tra un metodo definito su un’interfaccia e un metodo definito su una class base? Perché i diversi bytecode?

Anche la descrizione delle istruzioni sembra molto simile.

L’articolo sembra sostenere che la tabella dei metodi di un’interfaccia può avere “offset diversi” ogni volta che viene chiamato un metodo. Quello che non capisco è perché un’interfaccia dovrebbe avere una tabella dei metodi, poiché nessun object può avere l’interfaccia come tipo effettivo.

Cosa mi manca?

Ogni class Java è associata a una tabella di metodi virtuale che contiene “collegamenti” al bytecode di ciascun metodo di una class. Quella tabella è ereditata dalla superclass di una class particolare ed estesa rispetto ai nuovi metodi di una sottoclass. Per esempio,

 class BaseClass { public void method1() { } public void method2() { } public void method3() { } } class NextClass extends BaseClass { public void method2() { } // overridden from BaseClass public void method4() { } } 

risultati nelle tabelle

  BaseClass
 1. BaseClass / method1 ()
 2. BaseClass / method2 ()
 3. BaseClass / method3 ()

 NextClass
 1. BaseClass / method1 ()
 2. NextClass / method2 ()
 3. BaseClass / method3 ()
 4. NextClass / method4 () 

Nota, come la tabella del metodo virtuale di NextClass mantiene l’ordine delle voci della tabella di BaseClass e sovrascrive semplicemente il “link” di method2() che sovrascrive.

Un’implementazione della JVM può quindi ottimizzare una chiamata a invokevirtual ricordando che BaseClass/method3() sarà sempre la terza voce nella tabella dei metodi virtuali di qualsiasi object su cui questo metodo sarà mai invocato.

Con invokeinterface questa ottimizzazione non è ansible. Per esempio,

 interface MyInterface { void ifaceMethod(); } class AnotherClass extends NextClass implements MyInterface { public void method4() { } // overridden from NextClass public void ifaceMethod() { } } class MyClass implements MyInterface { public void method5() { } public void ifaceMethod() { } } 

Questa gerarchia di classi risulta nelle tabelle dei metodi virtuali

  AnotherClass
 1. BaseClass / method1 ()
 2. NextClass / method2 ()
 3. BaseClass / method3 ()
 4. AnotherClass / method4 ()
 5. MyInterface / ifaceMethod ()

 La mia class
 1. MyClass / method5 ()
 2. MyInterface / ifaceMethod () 

Come puoi vedere, AnotherClass contiene il metodo dell’interfaccia nella sua quinta voce e MyClass contiene nella sua seconda voce. Per trovare effettivamente la voce corretta nella tabella del metodo virtuale, una chiamata a un metodo con invokeinterface dovrà sempre cercare nella tabella completa senza una possibilità per lo stile di ottimizzazione che invokevirtual fa.

Ci sono altre differenze come il fatto che invokeinterface può essere usato insieme a riferimenti a oggetti che non implementano effettivamente l’interfaccia. Pertanto, invokeinterface dovrà verificare in fase di esecuzione se esiste un metodo nella tabella e potenzialmente generare un’eccezione. Se vuoi approfondire l’argomento, suggerisco, ad esempio, “Implementazione efficiente delle interfacce Java: Invokeinterface Considerato innocuo” .

Confrontando entrambe le istruzioni nella specifica JVM , la prima differenza è che invokevirtual verifica l’accessibilità del metodo durante la ricerca, mentre invokeinterface non lo fa.