Risposte:
Si tratta di una nuova istruzione JVM che consente a un compilatore di generare codice che chiama metodi con una specifica più libera di quanto fosse possibile in precedenza - se sai cos'è la " tipizzazione anatra ", invokedynamic sostanzialmente consente la tipizzazione anatra. Non c'è molto che tu possa fare un programmatore Java; se sei un creatore di strumenti, tuttavia, puoi utilizzarlo per creare linguaggi basati su JVM più flessibili ed efficienti. Ecco un post sul blog davvero dolce che offre molti dettagli.
MethodHandle
, che è davvero lo stesso tipo di cosa ma con molta più flessibilità. Ma il vero potere in tutto ciò non deriva dalle aggiunte al linguaggio Java, ma dalle capacità della JVM stessa nel supportare altri linguaggi intrinsecamente più dinamici.
invokedynamic
che li rendono performanti (rispetto a avvolgerli in una classe interna anonima che era quasi l'unica scelta prima di introdurre invokedynamic
). Molto probabilmente molti linguaggi di programmazione funzionale su JVM sceglieranno di compilare questo invece di classi anon-inner.
Qualche tempo fa, C # ha aggiunto una funzione interessante, sintassi dinamica all'interno di C #
Object obj = ...; // no static type available
dynamic duck = obj;
duck.quack(); // or any method. no compiler checking.
Pensalo come zucchero di sintassi per chiamate a metodi riflessivi. Può avere applicazioni molto interessanti. vedi http://www.infoq.com/presentations/StATIC-Dynamic-Typing-Neal-Gafter
Neal Gafter, che è responsabile del tipo dinamico di C #, è appena passato da SUN a MS. Quindi non è irragionevole pensare che le stesse cose siano state discusse in SUN.
Ricordo subito dopo che alcuni tizi di Java hanno annunciato qualcosa di simile
InvokeDynamic duck = obj;
duck.quack();
Sfortunatamente, la funzione non è dove trovarsi in Java 7. Molto deluso. Per i programmatori Java, non hanno modo di trarre vantaggio dai invokedynamic
loro programmi.
invokedynamic
non è mai stato progettato per essere utilizzato per programmatori Java. IMO non si adatta affatto alla filosofia Java. È stato aggiunto come funzionalità JVM per linguaggi non Java.
Ci sono due concetti da capire prima di continuare a invocinodinamico.
1. Digitazione statica vs. Dynamin
Statico - controllo del tipo di preforme al momento della compilazione (ad es. Java)
Dinamico : controllo del tipo di preforme in fase di esecuzione (ad es. JavaScript)
Il controllo del tipo è un processo per verificare che un programma sia sicuro, ovvero per verificare le informazioni digitate per variabili di classe e di istanza, parametri di metodo, valori restituiti e altre variabili. Ad esempio Java conosce int, String, .. al momento della compilazione, mentre il tipo di un oggetto in JavaScript può essere determinato solo in fase di esecuzione
2. Digitazione forte vs. debole
Forte : specifica le restrizioni sui tipi di valori forniti alle sue operazioni (ad es. Java)
Debole : converte (cast) argomenti di un'operazione se tali argomenti hanno tipi incompatibili (ad esempio Visual Basic)
Sapendo che Java è tipicamente e tipicamente debole, come si implementano i linguaggi tipizzati dinamicamente e fortemente sulla JVM?
Invokedynamic implementa un sistema di runtime che può scegliere l'implementazione più appropriata di un metodo o di una funzione - dopo che il programma è stato compilato.
Esempio: Avere (a + b) e non sapere nulla delle variabili a, b al momento della compilazione, invokedynamic mappa questa operazione al metodo più appropriato in Java in fase di runtime. Ad esempio, se risulta che a, b sono stringhe, quindi chiamare il metodo (stringa a, stringa b). Se risulta che a, b sono ints, quindi chiama il metodo (int a, int b).
invokedynamic è stato introdotto con Java 7.
Come parte del mio articolo su Java Records , ho articolato la motivazione alla base di Inoke Dynamic. Cominciamo con una definizione approssimativa di Indy.
Invoke Dynamic (noto anche come Indy ) faceva parte di JSR 292 con l' intenzione di migliorare il supporto JVM per Dynamic Type Languages. Dopo la sua prima versione in Java 7, The invokedynamic
opcode insieme al suo java.lang.invoke
bagaglio è ampiamente utilizzato da linguaggi dinamici basati su JVM come JRuby.
Sebbene Indy sia progettato specificamente per migliorare il supporto del linguaggio dinamico, offre molto di più. È un dato di fatto, è adatto all'uso ovunque un designer linguistico abbia bisogno di qualsiasi forma di dinamica, dalle acrobazie di tipo dinamico alle strategie dinamiche!
Ad esempio, le espressioni lambda Java 8 vengono effettivamente implementate utilizzando invokedynamic
, anche se Java è un linguaggio tipicamente statico!
Per un po 'di tempo JVM ha supportato quattro tipi di invocazione di metodi: invokestatic
chiamare metodi statici, invokeinterface
chiamare metodi di interfaccia, invokespecial
chiamare costruttori super()
o metodi privati e invokevirtual
chiamare metodi di istanza.
Nonostante le loro differenze, questi tipi di invocazione condividono un tratto comune: non possiamo arricchirli con la nostra logica . Al contrario, invokedynamic
ci consente di avviare il processo di invocazione nel modo che desideriamo. Quindi la JVM si occupa di chiamare direttamente il metodo Bootstrapped.
La prima volta che JVM vede invokedynamic
un'istruzione, chiama uno speciale metodo statico chiamato Bootstrap Method . Il metodo bootstrap è un pezzo di codice Java che abbiamo scritto per preparare l'attuale logica da invocare:
Quindi il metodo bootstrap restituisce un'istanza di java.lang.invoke.CallSite
. Questo CallSite
contiene un riferimento al metodo effettivo, ovvero MethodHandle
.
Da ora in poi, ogni volta che JVM vede di invokedynamic
nuovo queste istruzioni, salta il percorso lento e chiama direttamente l'eseguibile sottostante. La JVM continua a saltare il percorso lento a meno che qualcosa non cambi.
Java 14 Records
fornisce una bella sintassi compatta per dichiarare le classi che dovrebbero essere titolari di dati stupidi.
Considerando questo semplice record:
public record Range(int min, int max) {}
Il bytecode per questo esempio sarebbe qualcosa del tipo:
Compiled from "Range.java"
public java.lang.String toString();
descriptor: ()Ljava/lang/String;
flags: (0x0001) ACC_PUBLIC
Code:
stack=1, locals=1, args_size=1
0: aload_0
1: invokedynamic #18, 0 // InvokeDynamic #0:toString:(LRange;)Ljava/lang/String;
6: areturn
Nella sua tabella dei metodi Bootstrap :
BootstrapMethods:
0: #41 REF_invokeStatic java/lang/runtime/ObjectMethods.bootstrap:
(Ljava/lang/invoke/MethodHandles$Lookup;Ljava/lang/String;
Ljava/lang/invoke/TypeDescriptor;Ljava/lang/Class;
Ljava/lang/String;[Ljava/lang/invoke/MethodHandle;)Ljava/lang/Object;
Method arguments:
#8 Range
#48 min;max
#50 REF_getField Range.min:I
#51 REF_getField Range.max:I
Quindi viene chiamato il metodo bootstrap per Records bootstrap
che risiede nella java.lang.runtime.ObjectMethods
classe. Come puoi vedere, questo metodo bootstrap prevede i seguenti parametri:
MethodHandles.Lookup
rappresentazione del contesto di ricerca (La Ljava/lang/invoke/MethodHandles$Lookup
parte).toString
, equals
, hashCode
, etc.) il bootstrap sta per collegamento. Ad esempio, quando il valore è toString
, bootstrap restituirà un ConstantCallSite
(a CallSite
che non cambia mai) che punta toString
all'implementazione effettiva per questo particolare Record.TypeDescriptor
per il metodo ( Ljava/lang/invoke/TypeDescriptor
parte).Class<?>
rappresenta il tipo di classe Record. È
Class<Range>
in questo caso.min;max
.MethodHandle
per componente. In questo modo il metodo bootstrap può creare un MethodHandle
basato sui componenti per questa particolare implementazione del metodo.L' invokedynamic
istruzione passa tutti quegli argomenti al metodo bootstrap. Il metodo Bootstrap, a sua volta, restituisce un'istanza di ConstantCallSite
. Questo ConstantCallSite
contiene un riferimento all'implementazione del metodo richiesto, ad es toString
.
A differenza delle API di Reflection, l' java.lang.invoke
API è abbastanza efficiente poiché JVM può vedere completamente tutte le invocazioni. Pertanto, JVM può applicare tutti i tipi di ottimizzazioni purché evitiamo il percorso lento il più possibile!
Oltre all'argomento dell'efficienza, l' invokedynamic
approccio è più affidabile e meno fragile a causa della sua semplicità .
Inoltre, il bytecode generato per Java Records è indipendente dal numero di proprietà. Quindi, meno bytecode e tempi di avvio più rapidi.
Supponiamo infine che una nuova versione di Java includa un'implementazione del metodo bootstrap nuova e più efficiente. Con invokedynamic
, la nostra app può sfruttare questo miglioramento senza ricompilazione. In questo modo abbiamo una sorta di compatibilità binaria diretta . Inoltre, questa è la strategia dinamica di cui stavamo parlando!
Oltre a Java Records, la dinamica invoke è stata utilizzata per implementare funzionalità come:
LambdaMetafactory
StringConcatFactory
meth.invoke(args)
. Quindi come siinvokedynamic
adattameth.invoke
?