Eu sei que uma função embutida pode melhorar o desempenho e fazer com que o código gerado cresça, mas não tenho certeza de quando é correto usar uma.
lock(l) { foo() }
Em vez de criar um objeto de função para o parâmetro e gerar uma chamada, o compilador pode emitir o código a seguir. ( Fonte )
l.lock()
try {
foo()
}
finally {
l.unlock()
}
mas descobri que não há nenhum objeto de função criado por kotlin para uma função não embutida. porque?
/**non-inline function**/
fun lock(lock: Lock, block: () -> Unit) {
lock.lock();
try {
block();
} finally {
lock.unlock();
}
}
function
kotlin
inline-functions
holi-java
fonte
fonte
Respostas:
Digamos que você crie uma função de ordem superior que recebe um lambda do tipo
() -> Unit
(sem parâmetros, sem valor de retorno) e o executa assim:Na linguagem Java, isso se traduzirá em algo assim (simplificado!):
E quando você liga de Kotlin ...
Nos bastidores, uma instância de
Function
será criada aqui, que envolve o código dentro do lambda (novamente, isso é simplificado):Então, basicamente, chamar essa função e passar um lambda para ela sempre criará uma instância de um
Function
objeto.Por outro lado, se você usar a
inline
palavra-chave:Quando você chama assim:
Nenhuma
Function
instância será criada; em vez disso, o código em torno da invocação deblock
dentro da função embutida será copiado para o site da chamada, então você obterá algo assim no bytecode:Nesse caso, nenhuma nova instância é criada.
fonte
noinline
ecrossinline
- consulte os documentos .Deixe-me adicionar: "Quando não usar
inline
" :1) Se você tem uma função simples que não aceita outras funções como argumento, não faz sentido embuti-las. O IntelliJ irá avisá-lo:
2) Mesmo se você tiver uma função "com parâmetros de tipos funcionais", você pode encontrar o compilador dizendo que o inlining não funciona. Considere este exemplo:
Este código não compilará com o erro:
O motivo é que o compilador não consegue embutir esse código. Se
operation
não estiver envolvido em um objeto (o que está implícito,inline
visto que você deseja evitar isso), como pode ser atribuído a uma variável? Nesse caso, o compilador sugere fazer o argumentonoinline
. Ter umainline
função com uma únicanoinline
função não faz sentido, não faça isso. No entanto, se houver vários parâmetros de tipos funcionais, considere embutir alguns deles, se necessário.Então, aqui estão algumas regras sugeridas:
noinline
para os outros.reified
parâmetros de tipo, que exigem seu usoinline
. Leia aqui .fonte
inline
pode ser prejudicial é quando o parâmetro funcional é chamado várias vezes na função embutida, como em diferentes ramificações condicionais. Acabei de encontrar um caso em que todo o bytecode para argumentos funcionais estava sendo duplicado por causa disso.O caso mais importante quando usamos o modificador embutido é quando definimos funções do tipo utilitário com funções de parâmetro. O processamento de coleção ou string (como
filter
,map
oujoinToString
) ou apenas funções autônomas são um exemplo perfeito.É por isso que o modificador embutido é principalmente uma otimização importante para desenvolvedores de bibliotecas. Eles devem saber como funciona e quais são suas melhorias e custos. Devemos usar o modificador embutido em nossos projetos quando definimos nossas próprias funções util com parâmetros de tipo de função.
Se não tivermos parâmetro de tipo de função, parâmetro de tipo reificado e não precisarmos de retorno não local, provavelmente não devemos usar o modificador embutido. É por isso que teremos um aviso no Android Studio ou IDEA IntelliJ.
Além disso, há um problema de tamanho do código. O inlining de uma função grande pode aumentar drasticamente o tamanho do bytecode porque ele é copiado para cada site de chamada. Nesses casos, você pode refatorar a função e extrair o código para funções regulares.
fonte
As funções de ordem superior são muito úteis e podem realmente melhorar o funcionamento
reusability
do código. No entanto, uma das maiores preocupações em usá-los é a eficiência. As expressões lambda são compiladas em classes (geralmente classes anônimas), e a criação de objetos em Java é uma operação pesada. Ainda podemos usar funções de ordem superior de forma eficaz, mantendo todos os benefícios, tornando as funções embutidas.aqui vem a função embutida em imagem
Quando uma função é marcada como
inline
, durante a compilação do código, o compilador substituirá todas as chamadas de função pelo corpo real da função. Além disso, as expressões lambda fornecidas como argumentos são substituídas por seu corpo real. Eles não serão tratados como funções, mas como código real.Resumindo: - Inline -> em vez de serem chamados, eles são substituídos pelo código do corpo da função em tempo de compilação ...
Em Kotlin, usar uma função como parâmetro de outra função (as chamadas funções de ordem superior) parece mais natural do que em Java.
Usar lambdas tem algumas desvantagens, entretanto. Como são classes anônimas (e, portanto, objetos), elas precisam de memória (e podem até aumentar a contagem geral de métodos do seu aplicativo). Para evitar isso, podemos embutir nossos métodos.
Do exemplo acima : - Essas duas funções fazem exatamente a mesma coisa - imprimir o resultado da função getString. Um está embutido e o outro não.
Se você verificar o código java descompilado, verá que os métodos são completamente idênticos. Isso ocorre porque a palavra-chave inline é uma instrução para o compilador copiar o código para o site de chamada.
No entanto, se estivermos passando qualquer tipo de função para outra função como abaixo:
Para resolver isso, podemos reescrever nossa função conforme abaixo:
Suponha que tenhamos uma função de ordem superior como abaixo:
Aqui, o compilador nos dirá para não usar a palavra-chave inline quando houver apenas um parâmetro lambda e o estivermos passando para outra função. Portanto, podemos reescrever a função acima como abaixo:
Nota : - tivemos que remover a palavra-chave noinline também porque ela só pode ser usada para funções inline!
Suponha que tenhamos uma função como esta ->
Isso funciona bem, mas a essência da lógica da função está poluída com o código de medição, tornando mais difícil para seus colegas trabalharem o que está acontecendo. :)
Veja como uma função embutida pode ajudar neste código:
Agora posso me concentrar em ler qual é a principal intenção da função intercept () sem pular as linhas do código de medição. Também nos beneficiamos da opção de reutilizar esse código em outros lugares onde queremos
inline permite que você chame uma função com um argumento lambda dentro de um encerramento ({...}) em vez de passar a medida lambda like (myLamda)
fonte
Um caso simples onde você pode querer um é quando você cria uma função util que leva em um bloco suspenso. Considere isto.
Nesse caso, nosso cronômetro não aceitará funções de suspensão. Para resolver isso, você pode ficar tentado a suspendê-lo também
Mas então ele só pode ser usado a partir das próprias co-rotinas / funções suspensas. Então, você acabará fazendo uma versão assíncrona e uma versão não assíncrona desses utilitários. O problema desaparece se você torná-lo embutido.
Aqui está um playground kotlin com o estado de erro. Faça o cronômetro embutido para resolvê-lo.
fonte