Kotlin From Scratch Plus de plaisir avec des fonctions

Kotlin est un langage de programmation moderne qui compile en bytecode Java. C'est gratuit et open source, et promet de rendre le codage pour Android encore plus amusant.  

Dans l'article précédent, vous avez découvert les packages et les fonctions de base de Kotlin. Les fonctions sont au cœur de Kotlin, nous allons donc les examiner de plus près dans cet article. Nous allons explorer les types de fonctions suivants dans Kotlin:

  • fonctions de haut niveau
  • expressions lambda ou littéraux de fonction
  • fonctions anonymes
  • fonctions locales ou imbriquées
  • fonctions infixes
  • fonctions membres

Vous serez surpris par toutes les choses cool que vous pouvez faire avec les fonctions de Kotlin!

1. Fonctions de haut niveau

Les fonctions de niveau supérieur sont des fonctions à l'intérieur d'un package Kotlin qui sont définies en dehors de toute classe, objet ou interface. Cela signifie que ce sont des fonctions que vous appelez directement, sans qu'il soit nécessaire de créer un objet ou d'appeler une classe.. 

Si vous êtes un codeur Java, vous savez que nous créons généralement des méthodes statiques utilitaires dans des classes auxiliaires. Ces classes auxiliaires ne font vraiment rien. Elles n'ont aucune méthode d'instance ou d'état et servent simplement de conteneur pour les méthodes statiques. Un exemple typique est le Des collections classe dans le java.util package et ses méthodes statiques. 

Les fonctions de niveau supérieur de Kotlin peuvent être utilisées en remplacement des méthodes d'utilitaires statiques dans les classes d'assistance codées en Java. Voyons comment définir une fonction de niveau supérieur dans Kotlin. 

package com.chikekotlin.projectx.utils fun checkUserStatus (): String return "en ligne"

Dans le code ci-dessus, nous avons défini un paquet com.chikekotlin.projectx.utils à l'intérieur d'un fichier appelé UserUtils.kt et également défini une fonction utilitaire de niveau supérieur appelée checkUserStatus () à l'intérieur de ce même paquet et fichier. Par souci de brièveté, cette fonction très simple renvoie la chaîne "en ligne". 

Nous allons ensuite utiliser cette fonction utilitaire dans un autre package ou fichier..

package com.chikekotlin.projectx.users importer com.chikekotlin.projectx.utils.checkUserStatus if (checkUserStatus () == "en ligne") // faire quelque chose

Dans le code précédent, nous avons importé la fonction dans un autre paquet, puis nous l'avons exécuté! Comme vous pouvez le constater, il n'est pas nécessaire de créer un objet ou de référencer une classe pour appeler cette fonction..

Interopérabilité Java

Étant donné que Java ne prend pas en charge les fonctions de niveau supérieur, le compilateur Kotlin crée une classe Java en coulisse et les fonctions de niveau supérieur individuelles sont converties en méthodes statiques. Dans notre propre cas, la classe Java générée était UserUtilsKt avec une méthode statique checkUserStatus ()

/ * Java * / package com.chikekotlin.projectx.utils public class UserUtilsKt public static String checkUserStatus () return "en ligne"; 

Cela signifie que les appelants Java peuvent simplement appeler la méthode en référençant sa classe générée, comme pour toute autre méthode statique..

/ * Java * / import com.chikekotlin.projectx.utils.UserUtilsKt… UserUtilsKt.checkUserStatus ()

Notez que nous pouvons changer le nom de la classe Java généré par le compilateur Kotlin en utilisant le @JvmName annotation.

@file: package JvmName ("UserUtils") avec com.chikekotlin.projectx.utils fun checkUserStatus (): String return "en ligne"

Dans le code ci-dessus, nous avons appliqué le @JvmName annotation et spécifié un nom de classe UserUtilspour le fichier généré. Notez également que cette annotation est placée au début du fichier Kotlin, avant la définition du package.. 

Il peut être référencé depuis Java comme ceci:

/ * Java * / import com.chikekotlin.projectx.utils.UserUtils… UserUtils.checkUserStatus ()

2. Expressions Lambda

Les expressions lambda (ou les littéraux de fonction) ne sont également liés à aucune entité telle qu'une classe, un objet ou une interface. Ils peuvent être passés en tant qu'arguments à d'autres fonctions appelées fonctions d'ordre supérieur (nous en discuterons plus en détail dans le prochain post). Une expression lambda représente simplement le bloc d'une fonction et son utilisation réduit le bruit dans notre code. 

Si vous êtes un codeur Java, vous savez que Java 8 et les versions ultérieures prennent en charge les expressions lambda. Pour utiliser des expressions lambda dans un projet prenant en charge les versions antérieures de Java telles que Java 7, 6 ou 5, nous pouvons utiliser la bibliothèque Retrolambda, très répandue.. 

L'un des avantages de Kotlin est que les expressions lambda sont prises en charge immédiatement. Lambda n'étant pas pris en charge par Java 6 ou 7, Kotlin crée une classe anonyme Java en arrière-plan pour permettre à Kotlin d'interagir avec ce dernier. Mais notez que créer une expression lambda dans Kotlin est très différent de celui en Java..

Voici les caractéristiques d'une expression lambda dans Kotlin:

  • Il doit être entouré d'accolades .
  • Il n'a pas le amusement mot-clé. 
  • Il n'y a pas de modificateur d'accès (privé, public ou protégé) car il n'appartient à aucune classe, objet ou interface..
  • Il n'a pas de nom de fonction. En d'autres termes, c'est anonyme. 
  • Aucun type de retour n'est spécifié car il sera déduit par le compilateur.
  • Les paramètres ne sont pas entourés de parenthèses ()

De plus, nous pouvons affecter une expression lambda à une variable, puis l'exécuter.. 

Créer des expressions lambda

Voyons maintenant quelques exemples d'expressions lambda. Dans le code ci-dessous, nous avons créé une expression lambda sans aucun paramètre et lui avons attribué une variable. message. Nous avons ensuite exécuté l'expression lambda en appelant message()

val message = println ("Hey, Kotlin est vraiment cool!") message () // "Hey, Kotlin est vraiment cool!"

Voyons également comment inclure des paramètres dans une expression lambda. 

val message = myString: String -> println (myString) message ("I love Kotlin") // message "I love Kotlin" ("Jusqu'où?") // "Jusqu'où?"

Dans le code ci-dessus, nous avons créé une expression lambda avec le paramètre myString, avec le type de paramètre Chaîne. Comme vous pouvez le voir, devant le type de paramètre, il y a une flèche: elle fait référence au corps lambda. En d'autres termes, cette flèche sépare la liste de paramètres du corps lambda. Pour le rendre plus concis, nous pouvons complètement ignorer le type de paramètre (déjà inféré par le compilateur). 

val message = myString -> println (myString) // sera toujours compilé

Pour avoir plusieurs paramètres, il suffit de les séparer par une virgule. Et rappelez-vous, nous ne plaçons pas la liste des paramètres entre parenthèses comme en Java. 

val addNumbers = number1: Int, number2: Int -> println ("Ajout de $ number1 et $ number2") val result = number1 + number2 println ("Le résultat est $ result") addNumbers (1, 3)

Cependant, notez que si les types de paramètre ne peuvent pas être déduits, ils doivent être spécifiés explicitement (comme dans cet exemple), sinon le code ne sera pas compilé..

Ajout de 1 et 3 Le résultat est 4

Passer des Lambdas à des fonctions

Nous pouvons passer des expressions lambda en tant que paramètres de fonctions: on les appelle "fonctions d'ordre supérieur", car ce sont des fonctions de fonctions. Ces types de fonctions peuvent accepter une fonction lambda ou anonyme comme paramètre: par exemple, dernier() fonction de collecte. 

Dans le code ci-dessous, nous avons passé une expression lambda au dernier() une fonction. (Si vous souhaitez un rappel sur les collections dans Kotlin, visitez le troisième tutoriel de cette série) Comme son nom l'indique, le dernier élément de la liste est renvoyé..  dernier() accepte une expression lambda en tant que paramètre et cette expression prend à son tour un argument de type Chaîne. Son corps de fonction sert de prédicat pour effectuer une recherche dans un sous-ensemble d'éléments de la collection. Cela signifie que l'expression lambda décidera quels éléments de la collection seront pris en compte lors de la recherche du dernier.

val stringList: List = listOf ("in", "the", "club") print (stringList.last ()) // imprimera "club" print (stringList.last (s: Chaîne -> s.length == 3) ) // imprimera "le"

Voyons comment rendre cette dernière ligne de code plus lisible.

stringList.last s: String -> s.length == 3 // compilera et imprimera également "le" 

Le compilateur Kotlin nous permet de supprimer les parenthèses de la fonction si le dernier argument de la fonction est une expression lambda. Comme vous pouvez le constater dans le code ci-dessus, nous avons été autorisés à le faire car le dernier et unique argument transmis à dernier() fonction est une expression lambda. 

De plus, nous pouvons le rendre plus concis en supprimant le type de paramètre.

stringList.last s -> s.length == 3 // compilera également print "le" 

Nous n'avons pas besoin de spécifier explicitement le type de paramètre, car le type de paramètre est toujours identique au type d'élément de collection. Dans le code ci-dessus, nous appelons dernier sur une liste de collection de Chaîne objets, de sorte que le compilateur Kotlin est assez intelligent pour savoir que le paramètre sera également un Chaîne type. 

le il Nom de l'argument

Nous pouvons même simplifier encore l'expression lambda en remplaçant l'argument de l'expression lambda par le nom d'argument généré automatiquement. il.

stringList.last it.length == 3

le il nom d'argument a été généré automatiquement parce que dernier peut accepter une expression lambda ou une fonction anonyme (nous y reviendrons bientôt) avec un seul argument et son type peut être inféré par le compilateur.  

Retour local dans les expressions lambda

Commençons par un exemple. Dans le code ci-dessous, nous passons une expression lambda au pour chaque() fonction invoquée sur le intList collection. Cette fonction va parcourir la collection et exécuter le lambda sur chaque élément de la liste. Si un élément est divisible par 2, il s’arrêtera et reviendra du lambda. 

fun SurroundFunction () val intList = listOf (1, 2, 3, 4, 5) intList.forEach if (it% 2 == 0) return println ("Fin de surroundFunction ()") SurroundFunction ( ) // rien ne s'est passé

L'exécution du code ci-dessus n'a peut-être pas donné le résultat escompté. En effet, cette instruction return ne renverra pas de lambda mais de la fonction contenant entourantFonction ()! Cela signifie que la dernière instruction de code dans le entourantFonction () n'exécutera pas. 

//… println ("Fin of surroundFunction ()") // Ceci ne s'exécutera pas //… 

Pour résoudre ce problème, nous devons lui indiquer explicitement la fonction à renvoyer à l'aide d'une étiquette ou d'une balise de nom.. 

fun SurroundFunction () val intList = listOf (1, 2, 3, 4, 5) intList.forEach if (it% 2 == 0) return @ forEach println ("Fin de surroundFunction ()") / / Maintenant, il va exécuter surroundingFunction () // print "End of surroundingFunction ()"

Dans le code mis à jour ci-dessus, nous avons spécifié la balise par défaut @pour chaque immédiatement après revenir mot-clé à l'intérieur du lambda. Nous avons maintenant demandé au compilateur de revenir du lambda au lieu de la fonction contenant entourantFonction (). Maintenant la dernière déclaration de entourantFonction () va exécuter. 

Notez que nous pouvons également définir notre propre étiquette ou étiquette de nom. 

 //… intList.forEach myLabel @ if (it% 2 == 0) return @ myLabel //… 

Dans le code ci-dessus, nous avons défini notre étiquette personnalisée appelée myLabel @ puis spécifié pour la revenir mot-clé. le @pour chaque étiquette générée par le compilateur pour la pour chaque la fonction n'est plus disponible car nous avons défini notre propre. 

Cependant, vous verrez bientôt comment ce problème de retour local peut être résolu sans étiquettes lorsque nous abordons sous peu les fonctions anonymes dans Kotlin..

3. Fonctions membres

Ce type de fonction est défini dans une classe, un objet ou une interface. L'utilisation des fonctions membres nous aide à modulariser davantage nos programmes. Voyons maintenant comment créer une fonction membre.

classe Circle fun CalculateArea (radius: Double): Double require (radius> 0, "Le radius doit être supérieur à 0") return Math.PI * Math.pow (radius, 2.0)

Cet extrait de code montre une classe Cercle (nous discuterons des classes Kotlin dans des publications ultérieures) qui a une fonction membre CalculateArea (). Cette fonction prend un paramètre rayon calculer l'aire d'un cercle.

Pour appeler une fonction membre, nous utilisons le nom de la classe ou de l'instance d'objet contenant un point, suivi du nom de la fonction, en passant éventuellement des arguments..

val circle = Circle () print (circle.calculateArea (4.5)) // affichera "63.61725123519331"

4. Fonctions anonymes

Une fonction anonyme est un autre moyen de définir un bloc de code pouvant être transmis à une fonction. Il n'est lié à aucun identifiant. Voici les caractéristiques d'une fonction anonyme dans Kotlin:

  • n'a pas de nom
  • est créé avec le amusement mot-clé
  • contient un corps de fonction
val stringList: List = listOf ("in", "the", "club") print (stringList.last it.length == 3) // imprimera "le"

Parce que nous avons passé un lambda à la dernier() fonction ci-dessus, nous ne pouvons pas être explicite sur le type de retour. Pour être explicite sur le type de retour, nous devons utiliser une fonction anonyme à la place..

val strLenThree = stringList.last (fun (string): Boolean return string.length == 3) print (strLenThree) // affichera "le"

Dans le code ci-dessus, nous avons remplacé l'expression lambda par une fonction anonyme car nous souhaitons être explicites sur le type de retour. 

Vers la fin de la section lambda de ce tutoriel, nous avons utilisé une étiquette pour spécifier la fonction à partir de laquelle revenir. Utiliser une fonction anonyme au lieu d’un lambda dans le pour chaque() la fonction résout ce problème plus simplement. L’expression return renvoie de la fonction anonyme et non de la fonction environnante, qui dans notre cas est entourantFonction ().

fun SurroundFunction () val intList = listOf (1, 2, 3, 4, 5) intList.forEach (fun (nombre) if (nombre% 2 == 0) retour) println ("Fin de l’entourage ) ") // instruction exécutée surroundingFunction () // affichera" Fin de surroundFunction () "

5. Fonctions locales ou imbriquées

Pour aller plus loin dans la modularisation des programmes, Kotlin nous fournit des fonctions locales, également appelées fonctions imbriquées. Une fonction locale est une fonction déclarée dans une autre fonction.. 

fun printCircumferenceAndArea (rayon: Double): Unité funCalCirconférence (rayon: Double): Double = (2 * Math.PI) * rayon val circonference = "% .2f" .format (calCircumference (rayon)) fun calArea (rayon) Double): Double = (Math.PI) * Math.pow (rayon, 2.0) val area = "% .2f" .format (calArea (rayon)) print ("La circonférence du cercle de $ rayon rayon est $ circonférence et surface is $ area ") printCircumferenceAndArea (3.0) // La circonférence du cercle de 3.0 rayon est de 18.85 et la surface de 28.27

Comme vous pouvez le constater dans l'extrait de code ci-dessus, nous avons deux fonctions sur une seule ligne: calCircumference () et calArea () niché à l'intérieur du printCircumferenceAndAread () une fonction. Les fonctions imbriquées ne peuvent être appelées que de l'intérieur de la fonction englobante et non de l'extérieur. Encore une fois, l’utilisation de fonctions imbriquées rend notre programme plus modulaire et ordonné. 

Nous pouvons rendre nos fonctions locales plus concises en ne leur transmettant pas explicitement de paramètres. Cela est possible car les fonctions locales ont accès à tous les paramètres et variables de la fonction englobante. Voyons cela maintenant en action:

fun printCircumferenceAndArea (rayon: Double): Unité fun calCircumference (): double = (2 * Math.PI) * rayon val circonference = "% .2f" .format (calCircumference ()) fun calArea (): Double = (Math) .PI) * Math.pow (rayon, 2.0) val area = "% .2f" .format (calArea ()) //…

Comme vous pouvez le constater, ce code mis à jour semble plus lisible et réduit le bruit que nous avions auparavant. Bien que la fonction englobante dans cet exemple donnée soit petite, dans une fonction englobante plus grande qui peut être décomposée en fonctions imbriquées plus petites, cette fonctionnalité peut s'avérer très utile.. 

6. Fonctions infixes

le infixe La notation nous permet d’appeler facilement une fonction membre ou une fonction extension à un argument. En plus d’une fonction à un argument, vous devez également définir la fonction à l’aide de la touche infixe modificateur. Pour créer une fonction infixe, deux paramètres sont impliqués. Le premier paramètre est l'objet cible, tandis que le second paramètre est juste un paramètre unique transmis à la fonction. 

Création d'une fonction membre Infix

Voyons comment créer une fonction infixe dans une classe. Dans l'exemple de code ci-dessous, nous avons créé un Étudiant classe avec un mutable kotlinScore champ d'instance. Nous avons créé une fonction infixe en utilisant le infixe modificateur avant le amusement mot-clé. Comme vous pouvez le voir ci-dessous, nous avons créé une fonction infixe addKotlinScore () qui prend un score et ajoute à la kotlinScore champ d'instance. 

classe Student var kotlinScore = 0.0 infixe amusant addKotlinScore (score: Double): Unité this.kotlinScore = kotlinScore + score

Appeler une fonction infixe

Voyons également comment appeler la fonction infixe créée. Pour appeler une fonction infixe dans Kotlin, nous n’avons pas besoin d’utiliser la notation par points ni d’emballer le paramètre entre parenthèses.. 

val student = étudiant () étudiant addKotlinScore 95.00 print (student.kotlinScore) // imprimera "95.0"

Dans le code ci-dessus, nous avons appelé la fonction infixe, l’objet cible est étudiant, et le double 95.00 est le paramètre passé à la fonction. 

L'utilisation judicieuse des fonctions infixes peut rendre notre code plus expressif et plus clair que le style normal. Ceci est grandement apprécié lors de la rédaction de tests unitaires dans Kotlin (nous en discuterons dans un prochain article)..

"Chike" devrait commencerAvec ("ch") maListe devrait contenir (myElement) "Chike" devrait avoirLongueur (5) maMappe devrait avoir Clé (myKey) 

le à Fonction infixe

À Kotlin, nous pouvons faire la création d’un Paire par exemple plus succinct en utilisant le à fonction infixe au lieu de Paire constructeur. (Dans les coulisses, à crée également un Paire exemple.) Notez que le à function est aussi une fonction d'extension (nous en discuterons plus dans le prochain post).

infixe publique fun  A.à (que: B): paire = Paire (ça, ça)

Comparons maintenant la création d'un Paire exemple en utilisant à la fois le à fonction infixe et directement en utilisant le Paire constructeur, qui effectue la même opération, et voir lequel est le meilleur.

val nigeriaCallingCodePair = 234 à "Nigeria" val nigeriaCallingCodePair2 = Paire (234, "Nigeria") // Comme ci-dessus

Comme vous pouvez le voir dans le code ci-dessus, en utilisant le à fonction infixe est plus concise que directement en utilisant le Paire constructeur pour créer un Paire exemple. Rappelez-vous qu'en utilisant le à fonction infixe, 234 est l'objet cible et le Chaîne "Nigeria" est le paramètre passé à la fonction. En outre, notez que nous pouvons également le faire pour créer une Paire type:

val nigeriaCallingCodePair3 = 234.to ("Nigeria") // identique à l'utilisation de 234 en "Nigeria"

Dans l'article sur les gammes et les collections, nous avons créé une collection de cartes dans Kotlin en lui attribuant une liste de paires, la première valeur étant la clé et la seconde, la valeur. Comparons également la création d’une carte en utilisant à la fois la à fonction infixe et la Paire constructeur pour créer les paires individuelles.

val callingCodesMap: Carte = mapOf (234 à "Nigeria", 1 à "USA", 233 à "Ghana")

Dans le code ci-dessus, nous avons créé une liste de Paire types utilisant le à fonction infixe et les a transmis à la carte de() une fonction. Nous pouvons également créer la même carte en utilisant directement le Paire constructeur pour chaque paire.

val callingCodesPairMap: Carte = mapOf (Pair (234, "Nigeria"), Pair (1, "USA"), Pair (233, "Ghana"))

Comme vous pouvez le constater encore, rester avec le à fonction infixe a moins de bruit que l'utilisation de la Paire constructeur. 

Conclusion

Dans ce tutoriel, vous avez découvert certaines des fonctionnalités intéressantes que vous pouvez utiliser avec les fonctions de Kotlin. Nous avons couvert:

  • fonctions de haut niveau
  • expressions lambda ou littéraux de fonction
  • fonctions membres
  • fonctions anonymes
  • fonctions locales ou imbriquées
  • fonctions infixes

Mais ce n'est pas tout! Il reste encore beaucoup à apprendre sur les fonctions de Kotlin. Ainsi, dans le prochain article, vous apprendrez quelques utilisations avancées de fonctions, telles que les fonctions d'extension, les fonctions d'ordre supérieur et les fermetures. À bientôt!

Pour en savoir plus sur la langue Kotlin, je vous recommande de consulter la documentation Kotlin. Ou consultez certains de nos autres articles sur le développement d'applications Android ici sur Envato Tuts+!