3 février 2022 Nicolas Delauney

Parole d’expert : et si on passait enfin à Kotlin ?

Ici à ARCA, on croit à Kotlin comme successeur à Java. Langage propulsé par JetBrains et adoubé par Google, il est pourtant encore trop peu utilisé. Entre idées reçues et peur de l’avenir, beaucoup se refusent à passer le pas. Regardons ensemble pourquoi Kotlin est l’avenir du Java, et comment faire pour prendre ce virage.

# 1. Kotlin, kézako

Pour celles et ceux qui ne le sauraient pas, Kotlin est un langage développé par la compagnie JetBrains (derrière IntelliJ, PyCharm, Webstorm et consorts), qui prône la programmation orientée objet et la programmation fonctionnelle. La première version officielle est déployée en 2011, mais tout change en 2017 lorsque Google annonce que Kotlin est officiellement supporté pour Android, puis en 2019 lorsque la firme de Mountain View annonce que ce langage devient le langage officiel et recommandé pour Android. Autant dire que tous les développeurs Android y sont déjà passés. Cependant, Kotlin n’est pas réservé au développement mobile, et c’est ce que nous allons démontrer.

Pour la petite histoire, Kotlin est aussi une petite île russe (Ко́тлин), d’où l’image de couverture de cet article !

Seulement 3360km !

Seulement 3360km !

 

# 2. Idées reçues et contre arguments

• Le Kotlin est un langage fait pour Android

Situation :  un recruteur voit « Kotlin » sur un CV, et s’exclame « Parfait nous cherchons un développeur Android ! » (true story). Certes, Google en a fait le langage officiel d’Android, mais c’est aussi beaucoup plus que ça ! Kotlin est extrèmement versatile, et sur le site officiel Android n’est qu’en 4e position de cas d’utilisations. Kotlin est multi-plateforme, du javascript à l’iOS, et se compile en bytecode interprété directement par la JVM. Donc tout ce que Java fait, Kotlin fait. Android inclus, mais aussi bien plus…

• Je n’ai pas les compétences [dans mon équipe] pour faire du Kotlin

Je répète : Kotlin compile vers la JVM ! Donc, si vous savez faire du Java, vous savez faire du Kotlin. Enfin, en partie. Il y a évidemment des subtilités (par exemple, le type vient après la déclaration), mais la logique de classe et les méthodes peuvent être utilisées de la même manière. Nous reviendrons sur la syntaxe un peu plus tard. Certains IDE vous proposerons même une conversation automatique si vous collez un snippet Java dans un fichier Kotlin.

• J’ai déjà un projet en Java, ça couterait trop cher de le réécrire d’un coup

Jamais deux sans trois : Kotlin compile vers la JVM ! Ce qui veut dire qu’après un peu de configuration expliquée dans la documentation officielle, un projet peut mixer des fichiers *.java et *.kt en toute harmonie. Si on revient à l’histoire de migration, ça signifie que l’on peut tester sur une nouvelle feature en Kotlin sans toucher au projet Java, ou migrer les fichiers petits à petits. Une migration (presque) indolore, que demande le peuple ?

# 3. Exemples et introduction à Kotlin

Maintenant que tout cela est dit, qu’est-ce qu’on attend pour écrire du Kotlin ? Assez parlé de pourquoi vous devriez faire la transition. Voyons ensemble comment.

data class et immutabilité

Kotlin nous incite fortement à utiliser les data class plutôt que les class pour représenter de la donnée. Pourquoi ? Parce que les data class sont par défaut immutables, et hashables. Car Kotlin va générer automatiquement les méthodes equals(), hashCode(), et toString(). Voyez plutôt :

Data class en Kotlin
On notera l’utilisation de var : Kotlin possède deux types de déclaration. var pour variable et val pour valeur, qui correspond à un attribut final en Java. Mais si les data class sont immutables, comment les faire évoluer ? Grâce à une méthode générée copy, qui est en réalité un constructeur dont tous les paramètres sont optionnels et avec les attributs du parent comme valeur par défaut.

let, also, run, apply

Kotlin étant orienté programmation fonctionnelle, il y a toujours un moyen de chaîner nos méthodes. Et même si ce moyen n’existe pas, Kotlin met à disposition 4 méthodes indispensables qui prennent en paramètre une lambda : let, also, run et apply. Ils sont en réalité assez similaires :

let run also apply
Mais alors, pourquoi avoir fait des méthodes si similaires ? La différence entre la valeur de retour justifie une première duplication, et la différence entre this ou it se justifie pour éviter le shadowing si on imbrique des let et des run par exemple, mais au jour le jour les méthodes sont relativement interchangeables. Pour plus d’informations, voir la documentation officielle sur les Scope Functions. En contexte, cela peut donner la chose suivante :

Kotlin submarine
Oulah. Que de choses à dire sur si peu de lignes ! On a déjà vu les data class, et les var. fun permet de déclarer une méthode, comme ceci :

fun nom(parametres: Type): Type de retour { /* Code */ }

Ici le type de retour est Unit, qui est le type void en Kotlin.

  • Ligne 7, on réassigne la valeur de position, c’est possible car position est une variable (ligne 4). En  réalité, on réassigne this.position car en Kotlin, on peut omettre this, et l’écriture des getters et des setters est simplifiée automatiquement. La même ligne en Java donnerait donc this.setPosition(this.getPosition()...) et le reste du bloc à l’intérieur du setter. Bien plus verbeux.
  • Ligne 8, on utilise also pour afficher l’état. Position est donc assigné à it, que l’on voit utilisé dans la string qui sont toutes des templates string par défaut (d’où l’utilisation de $ devant it pour avoir l’interpolation)
  • Ligne 9 à 14, on ouvre le bloc run. Position est assigné à this, mais n’est utilisé nulle part dans le bloc ?? Le contexte se charge de l’interpolation. Ainsi, this.copy(...) devient copy(...), et this.getHorizontal() devient this.horizontal  puis seulement horizontal.
  • Enfin, la valeur de retour du bloc run est la valeur de retour de la méthode copy car c’est la dernière ligne du bloc.

Cet exemple est un peu costaud je le reconnais, mais réécrire la même chose en Java prendrait au moins le double de place, donc le double de temps à lire et à comprendre. Là le code est net, précis, simple.

C'est de toute beauté

Gestion des nulls

La gestion de la nullité dans Kotlin est native : plus besoin de tester si votre valeur est nulle partout dans votre code, il suffit de chainer avec un ?. si la valeur peut être nulle, et ?: pour expliciter quoi faire dans le cas null si nécessaire. Un petit exemple pour la route ? Le bloc takeIf renvoit la valeur de contexte si la condition est vraie, null sinon :

Deep blue

Infix, parlez à votre code !

Bon, celle là c’est un peu pour le fun. Mais Kotlin propose le mot clé infix, à placer devant la déclaration de méthode de classe, qui ne peut prendre qu’un seul paramètre. Si l’on reprend notre sous-marin, et que la méthode moveTo devient infix, et que l’on veut donner une série de positions successives à atteindre, voila ce que ça donnera :

infix
Les cas d’utilisations concrets ne sont pas légion, mais si Kotlin peut vous proposer une écriture plus simple à lire il le fera. On ne le répètera jamais assez, mais un code maintenable est avant tout un code lisible sans prise de tête !

Fonctions d’extension, de la bonne façon

Vous êtes vous déjà retrouvé(e)s dans la situation ou le domaine de l’application est bien défini, mais vous avez besoin de faire une opération sur le modèle dans un service en particulier ? En Java, deux solutions : soit il faut modifier le modèle, et faire rentrer du code utilitaire dans un objet auparavant bien pur et cohérent avec le métier ; soit il  faut mettre cette fonction utilitaire dans le service qui en a besoin, quitte à dupliquer cette malheureuse fonction dans tous les services nécessaires. Mouais.

Et Kotlin inventa les fonctions d’extensions…

Les fonctions d’extensions sont des fonctions déclarées au besoin sur une classe. Ces fonctions n’ont pas besoin d’être dans une classe, car contrairement à Java Kotlin permet d’écrire du code qui n’est pas Objet. Dans ce cas, ces méthodes seront visibles par tout le paquet, ou seulement par le fichier courant avec le mot clé private. Reprenons notre exemple de displayDeepBluePosition, qui ne fait aucun sens dans notre classe Position, mais est nécessaire pour un bout de notre programme. L’extension s’écrira alors :

Fonction d'extension

Ici, le paramètre de displayDeepBluePosition est toujours une Position, mais passé dans le contexte avec this. Notez qu’on aurait pu omettre le this ligne 6, et qu’il a été omis ligne 7. On a aussi omis le type de retour de la fonction, car Kotlin sait inférer le retour automatiquement.

 

#4 Mise en condition

Bon forcément, des exemples qui paraissent séduisants c’est sympa et c’est facile à écrire, mais qu’est-ce que ça donne sur du vrai code de production ? Dans l’équipe de rédaction, on a relevé le défi. On a pris un service écrit en Java, et on a créé son jumeau Kotlin. L’IDE nous a tout de suite prévenu : c’est le premier fichier Kotlin du projet, que voulez vous en faire ? Il nous a suffit de rajouter 3 lignes dans le pom.xml (1 si vous utilisez gradle) et le projet a accepté notre fichier comme un service valide. Facile !

pom.xmlBon et donc ? Évidemment, nous n’allons pas vous montrer tout le fichier, car ça serait illisible, mais voici ce que nous pouvons faire (pour le respect du code présenté, les noms de méthodes et de paramètres ont été modifiés).
En partant de cette méthode Java :
get papers - JAVA
On arrive à cette méthode Kotlin :
get papers - KOTLIN
Oui, je vous l’assure, ce code fait exactement la même chose. En 3 fois moins de lignes ! Et encore, le typage en Long alourdi le Kotlin, et ce typage vient du count() de Java. On pourrait s’en passer, mais ça serait changer la signature de la méthode alors on s’est dit que c’était de la triche. Globalement, le fichier passe de 515 lignes (23622 caractères) à 385 lignes (17030 caractères), soit environ 26% moins de code (donc à mon avis encore 285 lignes de trop mais ça c’est un autre débat). Et moins de lignes à coder, c’est moins de lignes à maintenir. Win win !


 

Voilà, on espère que cet article vous aura convaincu, et que l’argumentaire fera plier les plus réticents. Il y a vraiment peu de raisons à ne pas passer à Kotlin, et l’essayer c’est l’adopter ! Cet article couvre quelques subtilités de Kotlin, mais rassurez vous, il en reste encore beaucoup à découvrir.