Sigo escuchando acerca de todas las nuevas características interesantes que se están agregando a la JVM y una de esas características interesantes es invocada de forma dinámica. Me gustaría saber qué es y cómo hace que la programación reflexiva en Java sea más fácil o mejor.
fuente
meth.invoke(args)
. Entonces, ¿cómoinvokedynamic
encajameth.invoke
?MethodHandle
, que es realmente el mismo tipo de cosas pero con mucha más flexibilidad. Pero el verdadero poder de todo esto no se suma al lenguaje Java, sino a las capacidades de la propia JVM para admitir otros lenguajes que son intrínsecamente más dinámicos.invokedynamic
lo que lo hace eficiente (en comparación con envolverlas en una clase interna anónima que era casi la única opción antes de la introduccióninvokedynamic
). Lo más probable es que muchos lenguajes de programación funcionales además de JVM opten por compilar esto en lugar de anon-inner-classes.Hace algún tiempo, C # agregó una característica genial, sintaxis dinámica dentro de C #
Piense en ello como el azúcar de sintaxis para las llamadas a métodos reflexivos. Puede tener aplicaciones muy interesantes. ver http://www.infoq.com/presentations/Statical-Dynamic-Typing-Neal-Gafter
Neal Gafter, responsable del tipo dinámico de C #, acaba de desertar de SUN a MS. Por lo tanto, no es irracional pensar que se hayan discutido las mismas cosas dentro de SUN.
Recuerdo que poco después, un tipo de Java anunció algo similar
Desafortunadamente, la característica no se encuentra en Java 7. Muy decepcionado. Para los programadores de Java, no tienen una manera fácil de aprovechar
invokedynamic
sus programas.fuente
invokedynamic
nunca fue diseñado para ser utilizado por programadores Java. En mi opinión, no se ajusta a la filosofía de Java en absoluto. Se agregó como una función JVM para lenguajes no Java.Hay dos conceptos para entender antes de continuar invocando dinámicamente.
1. Mecanografía estática vs. Dynamin
Estático : comprobación de tipo de preformas en tiempo de compilación (por ejemplo, Java)
Dinámico : comprobación de tipo de preformas en tiempo de ejecución (por ejemplo, JavaScript)
La verificación de tipos es un proceso para verificar que un programa es seguro para los tipos, es decir, verificar la información escrita para las variables de clase e instancia, parámetros de método, valores de retorno y otras variables. Por ejemplo, Java conoce int, String, .. en tiempo de compilación, mientras que el tipo de un objeto en JavaScript solo se puede determinar en tiempo de ejecución
2. Mecanografía fuerte contra débil
Fuerte : especifica restricciones sobre los tipos de valores suministrados a sus operaciones (por ejemplo, Java)
Débil : convierte (convierte) los argumentos de una operación si esos argumentos tienen tipos incompatibles (por ejemplo, Visual Basic)
Sabiendo que Java es un tipo de escritura estática y débil, ¿cómo implementa lenguajes de tipo dinámico y fuerte en la JVM?
El invocador dinámico implementa un sistema de tiempo de ejecución que puede elegir la implementación más apropiada de un método o función, una vez que el programa ha sido compilado.
Ejemplo: Tener (a + b) y no saber nada sobre las variables a, b en tiempo de compilación, invoca dinámicamente esta operación al método más apropiado en Java en tiempo de ejecución. Por ejemplo, si resulta que a, b son Strings, entonces llame al método (String a, String b). Si resulta que a, b son ints, entonces llame al método (int a, int b).
invocador dinámico se introdujo con Java 7.
fuente
Como parte de mi artículo de Java Records , articulé sobre la motivación detrás de Inoke Dynamic. Comencemos con una definición aproximada de Indy.
Introduciendo Indy
Invoke Dynamic (también conocido como Indy ) era parte de JSR 292 con la intención de mejorar el soporte de JVM para Dynamic Type Languages. Después de su primer lanzamiento en Java 7, el
invokedynamic
código de operación junto con sujava.lang.invoke
equipaje es utilizado ampliamente por lenguajes dinámicos basados en JVM como JRuby.Aunque indy está específicamente diseñado para mejorar el soporte dinámico del lenguaje, ofrece mucho más que eso. De hecho, es adecuado para usar donde sea que un diseñador de idiomas necesite cualquier forma de dinámica, desde acrobacias de tipo dinámico hasta estrategias dinámicas.
Por ejemplo, las expresiones Lambda de Java 8 se implementan realmente usando
invokedynamic
, ¡aunque Java es un lenguaje estáticamente tipado!Código de bytes definible por el usuario
Durante bastante tiempo, JVM admitió cuatro tipos de invocación de métodos:
invokestatic
llamar a métodos estáticos,invokeinterface
llamar a métodos de interfaz,invokespecial
llamar a constructoressuper()
o métodos privados yinvokevirtual
llamar a métodos de instancia.A pesar de sus diferencias, estos tipos de invocación comparten un rasgo común: no podemos enriquecerlos con nuestra propia lógica . Por el contrario,
invokedynamic
nos permite Bootstrap el proceso de invocación de la forma que queramos. Luego, la JVM se encarga de llamar directamente al Método Bootstrapped.¿Cómo funciona Indy?
La primera vez que JVM ve una
invokedynamic
instrucción, llama a un método estático especial llamado Método Bootstrap . El método bootstrap es un fragmento de código Java que hemos escrito para preparar la lógica real a invocar:Luego, el método bootstrap devuelve una instancia de
java.lang.invoke.CallSite
. EstoCallSite
contiene una referencia al método real, es decirMethodHandle
.A partir de ahora, cada vez que JVM vuelve a ver esta
invokedynamic
instrucción, omite la ruta lenta y llama directamente al ejecutable subyacente. La JVM continúa omitiendo el camino lento a menos que algo cambie.Ejemplo: registros Java 14
Java 14
Records
proporciona una buena sintaxis compacta para declarar clases que se supone que son titulares de datos tontos.Considerando este simple registro:
El código de bytes para este ejemplo sería algo como:
En su tabla de método Bootstrap :
Entonces se llama al método bootstrap para Records
bootstrap
que reside en lajava.lang.runtime.ObjectMethods
clase. Como puede ver, este método de arranque espera los siguientes parámetros:MethodHandles.Lookup
representación del contexto de búsqueda (LaLjava/lang/invoke/MethodHandles$Lookup
parte).toString
,equals
,hashCode
, etc.) el bootstrap va a enlace. Por ejemplo, cuando el valor estoString
, bootstrap devolverá unConstantCallSite
(aCallSite
que nunca cambia) que apunta a latoString
implementación real de este Registro en particular.TypeDescriptor
para el método (Ljava/lang/invoke/TypeDescriptor
parte).Class<?>
, que representa el tipo de clase Record. EsClass<Range>
en este caso.min;max
.MethodHandle
por componente. De esta forma, el método bootstrap puede crear un métodoMethodHandle
basado en los componentes para la implementación de este método en particular.La
invokedynamic
instrucción pasa todos esos argumentos al método bootstrap. El método Bootstrap, a su vez, devuelve una instancia deConstantCallSite
. EstoConstantCallSite
contiene una referencia a la implementación del método solicitado, por ejemplotoString
.¿Por qué indy?
A diferencia de las API de Reflection, la
java.lang.invoke
API es bastante eficiente ya que la JVM puede ver completamente todas las invocaciones. Por lo tanto, JVM puede aplicar todo tipo de optimizaciones siempre que evitemos la ruta lenta tanto como sea posible.Además del argumento de la eficiencia, el
invokedynamic
enfoque es más confiable y menos frágil debido a su simplicidad .Además, el bytecode generado para Java Records es independiente del número de propiedades. Por lo tanto, menos bytecode y un tiempo de inicio más rápido.
Finalmente, supongamos que una nueva versión de Java incluye una implementación de método de arranque nueva y más eficiente. Con
invokedynamic
, nuestra aplicación puede aprovechar esta mejora sin recompilación. De esta manera tenemos algún tipo de compatibilidad binaria directa . Además, ¡esa es la estrategia dinámica de la que estábamos hablando!Otros ejemplos
Además de Java Records, la dinámica de invocación se ha utilizado para implementar características como:
LambdaMetafactory
StringConcatFactory
fuente