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.
invokedynamicche 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 invokedynamicloro programmi.
invokedynamicnon è 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 invokedynamicopcode insieme al suo java.lang.invokebagaglio è 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: invokestaticchiamare metodi statici, invokeinterfacechiamare metodi di interfaccia, invokespecialchiamare costruttori super()o metodi privati e invokevirtualchiamare 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 invokedynamicun'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 CallSitecontiene un riferimento al metodo effettivo, ovvero MethodHandle.
Da ora in poi, ogni volta che JVM vede di invokedynamicnuovo 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 Recordsfornisce 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 bootstrapche risiede nella java.lang.runtime.ObjectMethodsclasse. Come puoi vedere, questo metodo bootstrap prevede i seguenti parametri:
MethodHandles.Lookuprappresentazione del contesto di ricerca (La Ljava/lang/invoke/MethodHandles$Lookupparte).toString, equals, hashCode, etc.) il bootstrap sta per collegamento. Ad esempio, quando il valore è toString, bootstrap restituirà un ConstantCallSite(a CallSiteche non cambia mai) che punta toStringall'implementazione effettiva per questo particolare Record.TypeDescriptorper il metodo ( Ljava/lang/invoke/TypeDescriptor
parte).Class<?>rappresenta il tipo di classe Record. È
Class<Range>in questo caso.min;max.MethodHandleper componente. In questo modo il metodo bootstrap può creare un MethodHandlebasato sui componenti per questa particolare implementazione del metodo.L' invokedynamicistruzione passa tutti quegli argomenti al metodo bootstrap. Il metodo Bootstrap, a sua volta, restituisce un'istanza di ConstantCallSite. Questo ConstantCallSitecontiene un riferimento all'implementazione del metodo richiesto, ad es toString.
A differenza delle API di Reflection, l' java.lang.invokeAPI è 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' invokedynamicapproccio è 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:
LambdaMetafactoryStringConcatFactory
meth.invoke(args). Quindi come siinvokedynamicadattameth.invoke?