Eu continuo ouvindo sobre todos os novos recursos interessantes que estão sendo adicionados à JVM e um desses recursos interessantes é invocado dinâmico. Gostaria de saber o que é e como torna a programação reflexiva em Java mais fácil ou melhor?
java
reflection
jvm
invokedynamic
David K.
fonte
fonte
meth.invoke(args)
. Então, como seinvokedynamic
encaixameth.invoke
?MethodHandle
, que é realmente o mesmo tipo de coisa, mas com muito mais flexibilidade. Mas o poder real disso tudo não vem das adições à linguagem Java, mas dos recursos da própria JVM no suporte a outras linguagens que são intrinsecamente mais dinâmicas.invokedynamic
que a torna com desempenho (comparado a envolvê-las em uma classe interna anônima, que era quase a única opção antes da introduçãoinvokedynamic
). Provavelmente muitas linguagens de programação funcionais sobre a JVM optarão por compilar isso em vez de classes não internas.Há algum tempo, o C # adicionou um recurso interessante, sintaxe dinâmica dentro do C #
Pense nisso como açúcar de sintaxe para chamadas de método reflexivo. Pode ter aplicações muito interessantes. consulte http://www.infoq.com/presentations/Statically-Dynamic-Typing-Neal-Gafter
Neal Gafter, responsável pelo tipo dinâmico de C #, acabou de desertar de SUN para MS. Portanto, não é irracional pensar que as mesmas coisas foram discutidas no SUN.
Lembro-me logo depois disso, um cara Java anunciou algo semelhante
Infelizmente, o recurso não existe em Java 7. Muito decepcionado. Para programadores Java, eles não têm como tirar proveito de
invokedynamic
seus programas.fonte
invokedynamic
nunca foi planejado para ser usado por programadores Java. OMI não se encaixa na filosofia Java. Foi adicionado como um recurso da JVM para linguagens não Java.Existem dois conceitos a serem entendidos antes de continuar com o invocado dinâmico.
1. Digitação Estática vs. Dinamina
Estático - verificação de tipo de pré-formas em tempo de compilação (por exemplo, Java)
Dinâmico - verificação de tipo de pré-formas em tempo de execução (por exemplo, JavaScript)
A verificação de tipo é um processo para verificar se um programa é do tipo seguro, ou seja, verificar as informações digitadas para variáveis de classe e instância, parâmetros de método, valores de retorno e outras variáveis. Por exemplo, Java sabe sobre int, String, .. em tempo de compilação, enquanto o tipo de um objeto em JavaScript só pode ser determinado em tempo de execução
2. Digitação forte vs. fraca
Forte - especifica restrições sobre os tipos de valores fornecidos para suas operações (por exemplo, Java)
Fraco - converte (lança) argumentos de uma operação se esses argumentos tiverem tipos incompatíveis (por exemplo, Visual Basic)
Sabendo que o Java é do tipo Estático e Fraco, como você implementa linguagens do tipo Dinâmico e Fortemente na JVM?
O invokedynamic implementa um sistema de tempo de execução que pode escolher a implementação mais apropriada de um método ou função - após a compilação do programa.
Exemplo: Tendo (a + b) e sem saber nada sobre as variáveis a, b em tempo de compilação, invokedynamic mapeia essa operação para o método mais apropriado em Java em tempo de execução. Por exemplo, se for a, b são Strings, chame o método (String a, String b). Se, a, b são ints, chame o método (int a, int b).
invokedynamic foi introduzido com o Java 7.
fonte
Como parte do meu artigo da Java Records , articulei sobre a motivação por trás do Inoke Dynamic. Vamos começar com uma definição grosseira de Indy.
Apresentando Indy
Invoke Dynamic (também conhecido como Indy ) fazia parte do JSR 292 que pretendia aprimorar o suporte da JVM para idiomas de tipo dinâmico. Após seu primeiro lançamento em Java 7, o
invokedynamic
código de operação, juntamente com suajava.lang.invoke
bagagem, é amplamente utilizado por linguagens dinâmicas baseadas em JVM, como o JRuby.Embora o indy tenha sido projetado especificamente para aprimorar o suporte ao idioma dinâmico, oferece muito mais do que isso. Por uma questão de fato, é adequado para uso sempre que um designer de linguagem precisar de qualquer forma de dinâmica, de acrobacias de tipo dinâmico a estratégias dinâmicas!
Por exemplo, as Java 8 Lambda Expressions são realmente implementadas usando
invokedynamic
, mesmo que Java seja uma linguagem de tipo estaticamente!Bytecode Definível pelo Usuário
Por algum tempo, a JVM suportou quatro tipos de chamada de método:
invokestatic
chamar métodos estáticos,invokeinterface
chamar métodos de interface,invokespecial
chamar construtoressuper()
ou métodos privados einvokevirtual
chamar métodos de instância.Apesar de suas diferenças, esses tipos de invocação compartilham uma característica comum: não podemos enriquecê-los com nossa própria lógica . Pelo contrário,
invokedynamic
nos permite inicializar o processo de chamada da maneira que desejarmos. Em seguida, a JVM cuida de chamar o método Bootstrapped diretamente.Como o Indy funciona?
A primeira vez que a JVM vê uma
invokedynamic
instrução, ela chama um método estático especial chamado Método Bootstrap . O método bootstrap é uma parte do código Java que escrevemos para preparar a lógica real a ser invocada:Em seguida, o método de autoinicialização retorna uma instância de
java.lang.invoke.CallSite
. IssoCallSite
mantém uma referência ao método real, ieMethodHandle
.A partir de agora, toda vez que a JVM vir essa
invokedynamic
instrução novamente, ela ignora o Caminho Lento e chama diretamente o executável subjacente. A JVM continua ignorando o caminho lento, a menos que algo mude.Exemplo: Registros Java 14
O Java 14
Records
está fornecendo uma boa sintaxe compacta para declarar classes que deveriam ser detentoras de dados estúpidas.Considerando esse registro simples:
O bytecode para este exemplo seria algo como:
Em sua tabela de métodos de inicialização :
Portanto, o método de autoinicialização para registros é chamado,
bootstrap
que reside najava.lang.runtime.ObjectMethods
classe. Como você pode ver, esse método de inicialização espera os seguintes parâmetros:MethodHandles.Lookup
representação do contexto de pesquisa (ALjava/lang/invoke/MethodHandles$Lookup
parte).toString
,equals
,hashCode
, etc.) o bootstrap vai link. Por exemplo, quando o valor étoString
, o bootstrap retornará umConstantCallSite
(aCallSite
que nunca muda) que aponta para atoString
implementação real desse registro em particular.TypeDescriptor
para o método (Ljava/lang/invoke/TypeDescriptor
parte).Class<?>
, representando o tipo de classe Record. ÉClass<Range>
neste caso.min;max
.MethodHandle
por componente. Dessa maneira, o método de autoinicialização pode criar umaMethodHandle
base nos componentes para essa implementação de método específica.A
invokedynamic
instrução passa todos esses argumentos para o método de inicialização. O método Bootstrap, por sua vez, retorna uma instância deConstantCallSite
. IssoConstantCallSite
está mantendo uma referência à implementação do método solicitado, por exemplotoString
.Por que Indy?
Ao contrário das APIs de reflexão, a
java.lang.invoke
API é bastante eficiente, pois a JVM pode ver completamente todas as invocações. Portanto, a JVM pode aplicar todos os tipos de otimizações, desde que evitemos o caminho lento o máximo possível!Além do argumento da eficiência, a
invokedynamic
abordagem é mais confiável e menos quebradiça devido à sua simplicidade .Além disso, o bytecode gerado para Java Records é independente do número de propriedades. Portanto, menos bytecode e tempo de inicialização mais rápido.
Por fim, vamos supor que uma nova versão do Java inclua uma implementação de método de autoinicialização nova e mais eficiente. Com
invokedynamic
, nosso aplicativo pode tirar proveito dessa melhoria sem recompilação. Dessa forma, temos algum tipo de compatibilidade binária direta . Além disso, essa é a estratégia dinâmica de que estávamos falando!Outros exemplos
Além do Java Records, a dinâmica de chamada foi usada para implementar recursos como:
LambdaMetafactory
StringConcatFactory
fonte