Je continue d'entendre parler de toutes les nouvelles fonctionnalités intéressantes qui sont ajoutées à la JVM et l'une de ces fonctionnalités intéressantes est invokedynamic. J'aimerais savoir ce que c'est et comment cela rend-il la programmation réflexive en Java plus facile ou meilleure?
la source
meth.invoke(args)
. Alors, comment ça vainvokedynamic
avecmeth.invoke
?MethodHandle
, qui est vraiment le même genre de chose mais avec beaucoup plus de flexibilité. Mais la vraie puissance dans tout cela ne vient pas des ajouts au langage Java, mais des capacités de la JVM elle-même à prendre en charge d'autres langages qui sont intrinsèquement plus dynamiques.invokedynamic
ce qui le rend performant (par rapport à les envelopper dans une classe interne anonyme qui était presque le seul choix avant l'introductioninvokedynamic
). Très probablement, beaucoup de langages de programmation fonctionnels au-dessus de JVM choisiront de compiler vers cela au lieu de classes anon-internes.Il y a quelque temps, C # a ajouté une fonctionnalité intéressante, une syntaxe dynamique dans C #
Considérez-le comme du sucre de syntaxe pour les appels de méthode réflexifs. Cela peut avoir des applications très intéressantes. voir http://www.infoq.com/presentations/Statically-Dynamic-Typing-Neal-Gafter
Neal Gafter, qui est responsable du type dynamique de C #, vient de passer de SUN à MS. Il n'est donc pas déraisonnable de penser que les mêmes choses ont été discutées à l'intérieur de SUN.
Je me souviens que peu de temps après, un mec Java a annoncé quelque chose de similaire
Malheureusement, la fonctionnalité est introuvable dans Java 7. Très déçu. Pour les programmeurs Java, ils n'ont pas de moyen facile d'en tirer parti
invokedynamic
dans leurs programmes.la source
invokedynamic
n'a jamais été conçu pour être utilisé par les programmeurs Java. OMI, cela ne correspond pas du tout à la philosophie Java. Il a été ajouté en tant que fonctionnalité JVM pour les langages non Java.Il y a deux concepts à comprendre avant de continuer à invokedynamic.
1. Typage statique ou dynamin
Statique - vérification du type des préformes au moment de la compilation (par exemple Java)
Dynamique - vérification du type de préformes au moment de l'exécution (par exemple JavaScript)
La vérification de type est un processus de vérification de la sécurité d'un programme, c'est-à-dire la vérification des informations typées pour les variables de classe et d'instance, les paramètres de méthode, les valeurs de retour et d'autres variables. Par exemple, Java connaît int, String, .. au moment de la compilation, tandis que le type d'un objet en JavaScript ne peut être déterminé qu'au moment de l'exécution
2. Typage fort ou faible
Strong - spécifie les restrictions sur les types de valeurs fournies à ses opérations (par exemple Java)
Faible - convertit (caste) les arguments d'une opération si ces arguments ont des types incompatibles (par exemple, Visual Basic)
Sachant que Java est un langage statique et faiblement typé, comment implémentez-vous des langages dynamiquement et fortement typés sur la JVM?
Le invokedynamic implémente un système d'exécution qui peut choisir l'implémentation la plus appropriée d'une méthode ou d'une fonction - une fois le programme compilé.
Exemple: ayant (a + b) et ne sachant rien sur les variables a, b au moment de la compilation, invokedynamic mappe cette opération à la méthode la plus appropriée en Java au moment de l'exécution. Par exemple, s'il s'avère que a, b sont des chaînes, appelez la méthode (String a, String b). S'il s'avère que a, b sont des entiers, alors appelez la méthode (int a, int b).
invokedynamic a été introduit avec Java 7.
la source
Dans le cadre de mon article sur Java Records , j'ai expliqué la motivation derrière Inoke Dynamic. Commençons par une définition approximative d'Indy.
Présentation d'Indy
Invoke Dynamic (également connu sous le nom d' Indy ) faisait partie de JSR 292 visant à améliorer la prise en charge JVM des langages de type dynamique. Après sa première version en Java 7, l'
invokedynamic
opcode et sesjava.lang.invoke
bagages sont largement utilisés par les langages dynamiques basés sur JVM comme JRuby.Bien qu'indy soit spécialement conçu pour améliorer la prise en charge dynamique des langues, il offre bien plus que cela. En fait, il peut être utilisé partout où un concepteur de langage a besoin de toute forme de dynamicité, des acrobaties de type dynamique aux stratégies dynamiques!
Par exemple, les expressions Java 8 Lambda sont en fait implémentées en utilisant
invokedynamic
, même si Java est un langage de typage statique!Bytecode définissable par l'utilisateur
Pendant un certain temps, JVM a pris en charge quatre types d'invocation de méthode:
invokestatic
pour appeler des méthodes statiques,invokeinterface
pour appeler des méthodes d'interface,invokespecial
pour appeler des constructeurssuper()
ou des méthodes privées etinvokevirtual
pour appeler des méthodes d'instance.Malgré leurs différences, ces types d'invocation partagent un trait commun: nous ne pouvons pas les enrichir avec notre propre logique . Au contraire,
invokedynamic
nous permet d'amorcer le processus d'appel de la manière que nous voulons. Ensuite, la JVM se charge d'appeler directement la méthode Bootstrapped.Comment fonctionne Indy?
La première fois que JVM voit une
invokedynamic
instruction, elle appelle une méthode statique spéciale appelée Méthode Bootstrap . La méthode bootstrap est un morceau de code Java que nous avons écrit pour préparer la logique à appeler:Ensuite, la méthode bootstrap renvoie une instance de
java.lang.invoke.CallSite
. CelaCallSite
contient une référence à la méthode réelle, à savoirMethodHandle
.À partir de maintenant, chaque fois que JVM voit à
invokedynamic
nouveau cette instruction, il ignore le chemin lent et appelle directement l'exécutable sous-jacent. La machine virtuelle Java continue d'ignorer le chemin lent à moins que quelque chose ne change.Exemple: enregistrements Java 14
Java 14
Records
fournit une belle syntaxe compacte pour déclarer des classes censées être des détenteurs de données stupides.Compte tenu de ce simple enregistrement:
Le bytecode pour cet exemple serait quelque chose comme:
Dans son tableau des méthodes Bootstrap :
Ainsi, la méthode d' amorçage pour Records est appelée
bootstrap
qui réside dans lajava.lang.runtime.ObjectMethods
classe. Comme vous pouvez le voir, cette méthode de bootstrap attend les paramètres suivants:MethodHandles.Lookup
représentation du contexte de recherche (laLjava/lang/invoke/MethodHandles$Lookup
pièce).toString
,equals
,hashCode
, etc.) , le bootstrap va lien. Par exemple, lorsque la valeur esttoString
, bootstrap renverra unConstantCallSite
(aCallSite
qui ne change jamais) qui pointe vers l'toString
implémentation réelle de cet enregistrement particulier.TypeDescriptor
pour la méthode (Ljava/lang/invoke/TypeDescriptor
partie).Class<?>
représentant le type de classe Record. C'estClass<Range>
dans ce cas.min;max
.MethodHandle
par composant. De cette façon, la méthode bootstrap peut créer unMethodHandle
basé sur les composants pour cette implémentation de méthode particulière.L'
invokedynamic
instruction transmet tous ces arguments à la méthode bootstrap. La méthode Bootstrap, à son tour, retourne une instance deConstantCallSite
. CeciConstantCallSite
contient une référence à la mise en œuvre de la méthode demandée, par exempletoString
.Pourquoi Indy?
Contrairement aux API Reflection, l'
java.lang.invoke
API est assez efficace car la JVM peut voir complètement toutes les invocations. Par conséquent, JVM peut appliquer toutes sortes d'optimisations tant que nous évitons autant que possible le chemin lent!En plus de l'argument d'efficacité, l'
invokedynamic
approche est plus fiable et moins fragile en raison de sa simplicité .De plus, le bytecode généré pour les enregistrements Java est indépendant du nombre de propriétés. Donc, moins de bytecode et un temps de démarrage plus rapide.
Enfin, supposons qu'une nouvelle version de Java inclut une nouvelle implémentation de méthode de bootstrap plus efficace. Avec
invokedynamic
, notre application peut profiter de cette amélioration sans recompilation. De cette façon, nous avons une sorte de compatibilité binaire directe . C'est aussi la stratégie dynamique dont nous parlions!Autres exemples
En plus des enregistrements Java, la dynamique d' appel a été utilisée pour implémenter des fonctionnalités telles que:
LambdaMetafactory
StringConcatFactory
la source