Je sais qu'une fonction en ligne améliorera peut-être les performances et fera croître le code généré, mais je ne sais pas quand il est correct d'en utiliser une.
lock(l) { foo() }
Au lieu de créer un objet fonction pour le paramètre et de générer un appel, le compilateur peut émettre le code suivant. ( Source )
l.lock()
try {
foo()
}
finally {
l.unlock()
}
mais j'ai trouvé qu'il n'y a pas d'objet fonction créé par kotlin pour une fonction non en ligne. Pourquoi?
/**non-inline function**/
fun lock(lock: Lock, block: () -> Unit) {
lock.lock();
try {
block();
} finally {
lock.unlock();
}
}
function
kotlin
inline-functions
holi-java
la source
la source
Réponses:
Disons que vous créez une fonction d'ordre supérieur qui prend un lambda de type
() -> Unit
(pas de paramètres, pas de valeur de retour), et l'exécute comme ceci:En langage Java, cela se traduira par quelque chose comme ceci (simplifié!):
Et quand vous l'appelez de Kotlin ...
Sous le capot, une instance de
Function
sera créée ici, qui encapsule le code à l'intérieur du lambda (encore une fois, cela est simplifié):Donc, fondamentalement, appeler cette fonction et lui passer un lambda créera toujours une instance d'un
Function
objet.En revanche, si vous utilisez le
inline
mot - clé:Quand vous l'appelez comme ceci:
Aucune
Function
instance ne sera créée, à la place, le code autour de l'invocation de l'block
intérieur de la fonction en ligne sera copié sur le site d'appel, vous obtiendrez donc quelque chose comme ça dans le bytecode:Dans ce cas, aucune nouvelle instance n'est créée.
la source
noinline
etcrossinline
- consultez la documentation .Permettez-moi d'ajouter: "Quand ne pas utiliser
inline
" :1) Si vous avez une fonction simple qui n'accepte pas d'autres fonctions comme argument, cela n'a pas de sens de les insérer. IntelliJ vous avertira:
2) Même si vous avez une fonction "avec des paramètres de types fonctionnels", vous pouvez rencontrer le compilateur vous disant que l'inlining ne fonctionne pas. Prenons cet exemple:
Ce code ne se compilera pas avec l'erreur:
La raison en est que le compilateur ne parvient pas à insérer ce code. Si
operation
n'est pas enveloppé dans un objet (ce qui est impliqué parinline
puisque vous voulez éviter cela), comment peut-il être affecté à une variable? Dans ce cas, le compilateur suggère de faire l'argumentnoinline
. Avoir uneinline
fonction avec une seulenoinline
fonction n'a aucun sens, ne faites pas ça. Cependant, s'il existe plusieurs paramètres de types fonctionnels, envisagez d'en intégrer certains si nécessaire.Voici donc quelques règles suggérées:
noinline
pour les autres.reified
les paramètres de type, qui nécessitent que vous les utilisiezinline
. Lisez ici .la source
inline
peut être nuisible est lorsque le paramètre fonctionnel est appelé plusieurs fois dans la fonction en ligne, comme dans différentes branches conditionnelles. Je viens de tomber sur un cas où tout le bytecode des arguments fonctionnels était dupliqué à cause de cela.Le cas le plus important lorsque nous utilisons le modificateur en ligne est lorsque nous définissons des fonctions de type util avec des fonctions de paramètres. Le traitement de collection ou de chaîne (comme
filter
,map
oujoinToString
) ou simplement des fonctions autonomes en sont un exemple parfait.C'est pourquoi le modificateur en ligne est principalement une optimisation importante pour les développeurs de bibliothèques. Ils doivent savoir comment cela fonctionne, quelles sont ses améliorations et ses coûts. Nous devrions utiliser le modificateur en ligne dans nos projets lorsque nous définissons nos propres fonctions util avec des paramètres de type de fonction.
Si nous n'avons pas de paramètre de type de fonction, de paramètre de type réifié et que nous n'avons pas besoin de retour non local, nous ne devrions probablement pas utiliser le modificateur en ligne. C'est pourquoi nous aurons un avertissement sur Android Studio ou IDEA IntelliJ.
Il existe également un problème de taille de code. L'intégration d'une fonction volumineuse peut augmenter considérablement la taille du bytecode car il est copié sur chaque site d'appel. Dans de tels cas, vous pouvez refactoriser la fonction et extraire le code en fonctions régulières.
la source
Les fonctions d'ordre supérieur sont très utiles et peuvent vraiment améliorer le
reusability
code. Cependant, l'une des plus grandes préoccupations liées à leur utilisation est l'efficacité. Les expressions Lambda sont compilées en classes (souvent des classes anonymes) et la création d'objets en Java est une opération lourde. Nous pouvons toujours utiliser des fonctions d'ordre supérieur de manière efficace, tout en conservant tous les avantages, en rendant les fonctions en ligne.voici la fonction en ligne en image
Lorsqu'une fonction est marquée comme
inline
, lors de la compilation du code, le compilateur remplacera tous les appels de fonction par le corps réel de la fonction. De plus, les expressions lambda fournies en tant qu'arguments sont remplacées par leur corps réel. Ils ne seront pas traités comme des fonctions, mais comme du code réel.En bref: - Inline -> plutôt que d'être appelés, ils sont remplacés par le code du corps de la fonction au moment de la compilation ...
Dans Kotlin, utiliser une fonction comme paramètre d'une autre fonction (appelées fonctions d'ordre supérieur) semble plus naturel qu'en Java.
Cependant, l'utilisation de lambdas présente certains inconvénients. Puisqu'il s'agit de classes anonymes (et donc d'objets), elles ont besoin de mémoire (et peuvent même ajouter au nombre global de méthodes de votre application). Pour éviter cela, nous pouvons intégrer nos méthodes.
De l'exemple ci-dessus : - Ces deux fonctions font exactement la même chose - imprimer le résultat de la fonction getString. L'un est en ligne et l'autre ne l'est pas.
Si vous vérifiez le code java décompilé, vous verrez que les méthodes sont complètement identiques. C'est parce que le mot clé en ligne est une instruction au compilateur pour copier le code dans le site d'appel.
Cependant, si nous passons un type de fonction à une autre fonction comme ci-dessous:
Pour résoudre cela, nous pouvons réécrire notre fonction comme ci-dessous:
Supposons que nous ayons une fonction d'ordre supérieur comme ci-dessous:
Ici, le compilateur nous dira de ne pas utiliser le mot clé inline lorsqu'il n'y a qu'un seul paramètre lambda et que nous le passons à une autre fonction. Ainsi, nous pouvons réécrire la fonction ci-dessus comme ci-dessous:
Remarque : -nous avons également dû supprimer le mot-clé noinline car il ne peut être utilisé que pour les fonctions en ligne!
Supposons que nous ayons une fonction comme celle-ci ->
Cela fonctionne bien, mais le cœur de la logique de la fonction est pollué par le code de mesure, ce qui rend plus difficile pour vos collègues de travailler sur ce qui se passe. :)
Voici comment une fonction en ligne peut aider ce code:
Maintenant, je peux me concentrer sur la lecture de l'intention principale de la fonction intercept () sans sauter des lignes de code de mesure. Nous bénéficions également de la possibilité de réutiliser ce code dans d'autres endroits où nous voulons
inline vous permet d'appeler une fonction avec un argument lambda dans une fermeture ({...}) plutôt que de passer la mesure lambda like (myLamda)
la source
Un cas simple où vous pourriez en vouloir un est lorsque vous créez une fonction util qui prend dans un bloc de suspension. Considère ceci.
Dans ce cas, notre minuterie n'acceptera pas les fonctions de suspension. Pour le résoudre, vous pourriez être tenté de le faire suspendre également
Mais alors, il ne peut être utilisé qu'à partir des coroutines / fonctions de suspension elles-mêmes. Ensuite, vous finirez par créer une version asynchrone et une version non asynchrone de ces utilitaires. Le problème disparaît si vous le faites en ligne.
Voici un terrain de jeu kotlin avec l'état d'erreur. Rendez la minuterie en ligne pour le résoudre.
la source