Introduction au langage Kotlin

Ce tutoriel est une introduction au langage Kotlin pour les développeurs connaissant déjà le langage Java (ou autre langage orienté objet).

Article lu   fois.

L'auteur

Profil Pro

Liens sociaux

Viadeo Twitter Facebook Share on Google+   

I. Présentation et installation

I-A. Présentation

Le langage Kotlin est un langage développé par JetBrains, entreprise conceptrice de l'EDIEnvironement de Dévelopement Intégré Intellij Idea entre autres. Cependant il est open-source.

Ses principaux avantages résident dans la simplification du code pour de nombreuses tâches courantes (par exemple, la création de POJOPlain Old Java Object), une sécurité de code accrue grâce à un contrôle stricte de la nullité des valeurs, et une courbe d'apprentissage relativement faible (au moins pour les développeurs ayant déjà des prérequis en Java ou en POOProgrammation Orientée Objet de manière générale).

Le langage Kotlin est un langage qui a plusieurs déclinaisons :

  • une version visant une interaction avec le langage Java, et qui est compilé en bytecode afin d'être exécuté sur la JVMJava Virtual Machine ou sur les périphériques Android,
  • une version visant à être compilée pour le langage Javascript,
  • une version dite native, visant à être compilée directement en exécutable indépendant de toute plateforme d'interprétation.

Ce tutoriel a pour objectif l'enseignement de la version pour la JVMJava Virtual Machine, même s'il ne devrait pas être ardu de passer à l'une des deux autres versions.

Il vise principalement les développeurs étant familiers avec le langage Java et la POOProgrammation Orientée Objet : même si tout langage impératif et orienté objet devrait convenir.

I-B. Installation

Nous allons ici aborder l'installation du langage Kotlin, même s'il ne s'agit pas d'une installation proprement dite, étant donné que nous passons par l'outil GradleL'outil de build Gradle afin de récupérer les dépendances, compiler le programme et l'exécuter de manière automatique.

Sachez également qu'il est possible de tester les codes directement en ligneEssai de Kotlin en ligne (y compris pour les projets incluant plusieurs fichiers).

Ce tutoriel se basera sur l'éditeur Intellij IdeaEditeur Intellij Idea (la version Community est gratuite), mais est adaptable à tout éditeur capable de gérer les projets Gradle. Sachez aussi que pour mieux gérer les différentes versions et mises à jour d'Intellij Idea, vous disposez aussi du programme gratuit JetBrain Toolboxl'outil JetBrain Toolbox.

Par ailleurs, ce tutoriel est basé sur la version 1.2.10 de Kotlin et la version d'Intellij Idea Community 2017.3.4.

Afin d'installer l'environnement de développement, nous pouvons procéder comme suit :

  1. Nous utiliserons Kotlin à travers la JVM : nous avons donc besoin d'installer Java correspondant à notre Système d'Exploitation, si ce n'est déjà fait : Page de téléchargement du SDK Java 8Page de téléchargement de Java 8 (la version 8 de Java devrait être amplement suffisante),
  2. Nous pouvons installer la version d'Intellij Idea Community correspondant à notre système (ou passer par la JetBrains Toolbox afin de mieux gérer les mises à jour),
  3. Afin de bénéficier de la coloration syntaxique et de la vérification syntaxique du code par l'EDI, nous pouvons aussi installer le plugin Kotlin. Pour cela cliquez d'abord sur le bouton Configure dans l'accueil (fig 1.1), et choisissez le menu Plugins. Il se peut que vous ne soyez pas dans la fenêtre d'accueil, mais qu'un projet soit déjà chargé : dans ce cas il suffit simplement de refermer le projet (menu File→Close project) pour revenir à l'acceuil. Cliquez alors sur le bouton Browse repositories … (fig 1.2). Cherchez le plugin Kotlin et installez-le (fig 1.3), puis cliquez sur Restart Intellij IDEA (puis validez avec Ok puis Restart) une fois que cela est fait afin de prendre en compte les mises à jour.
Bouton Configure
Fig 1.1 Bouton Configure
Bouton Brose Repositories
Fig. 1.2 Bouton Brose Repositories
Plugin Kotlin
Fig. 1.3 Plugin Kotlin

I-C. Première prise en main

Nous allons créer un simple programme affichant « Bonjour, les développeurs ! » depuis Intellij Idea en Kotlin, sans rien installer de plus : grâce au logiciel Gradle intégré dans notre EDI.

I-C-1. Création du projet

  1. Démarrez un nouveau projet de type Gradle dans Intellij Idea : notez bien qu'il vous faudra éventuellement préciser le chemin de votre SDK Java. Vérifiez également que le projet soit uniquement de type Java (fig 1.4),
  2. Dans la fenêtre suivante, entrez com.developpez dans le champ GroupId (c'est le nom de domaine de l'application) et HelloWorld dans le champ ArtifactId (nom d'application),
  3. La plupart des paramètres par défaut de la 3e fenêtre devraient convenir :

    1. Cochez « Use autoimport » si ce n'est déjà fait
    2. Un module séparé par ensemble de sources
    3. Ne pas générer les fichiers du projet de manière externe
    4. L'exécutable Gradle par défaut doit convenir aussi
  4. Le nom du projet proposé est le même que l'ArtifactId renseigné dans la 2e fenêtre : il peut rester tel quel.
Projet de type java et gradle
Fig. 1.4 Projet de type java et gradle

I-C-2. Téléchargement automatique des dépendances

Maintenant remplacez le contenu du fichier build.gradle (ce qui nous sert à configurer la compilation via Gradle) par ceci :

Configuration du projet via Gradle
TéléchargerCacherSélectionnez
1.
2.
3.
4.
5.
6.
7.
8.
9.
10.
11.
12.
13.
14.
15.
16.
17.
18.
19.
20.
// On va telecharger le plugin Kotlin pour Gradle
plugins {
    id "org.jetbrains.kotlin.jvm" version "1.2.10"
}
// Nous voulons pouvoir executer ce projet en tant qu'application
apply plugin: 'application'
// Pourquoi la classe principale s'appelle-t-elle HelloKt ?
// Nous verrons sous peu.
mainClassName = "HelloKt"
// Nos identifiants pour le projet
group 'com.developpez'
version '1.0-SNAPSHOT'
// Ou aller chercher les dependances
repositories {
    mavenCentral()
}
// La dependance declaree est indispensable
dependencies {
    compile "org.jetbrains.kotlin:kotlin-stdlib"
}

Lorsque vous sauvegarderez ce fichier, Gradle téléchargera automatiquement (ou mettra à jour) les dépendances déclarées.

I-C-3. Ajout du fichier source et exécution

  1. Il nous faut d'abord créer le dossier Kotlin, même s'il sera automatiquement reconnu en tant que dossier source. Sélectionnez le dossier main dans la vue explorateur de fichiers (fig 1.5) et lancer son menu contextuel (bouton droit de la souris). Sélectionnez le menu New → Directory. Nommez le dossier kotlin (attention à la casse!).
  2. Lancez le menu contextuel sur le dossier kotlin juste créé et sélectionnez le menu New → Kotlin File/Class… et entrez le nom Hello (sans extension). Notez que le menu Kotlin File/Class… ne sera visible que si le plugin kotlin est activé.
  3. Entrez le code suivant dans le fichier Kotlin.kt :
Simple Hello World
TéléchargerSélectionnez
fun main(args: Array<String>){
    println("Bonjour, les développeurs !")
}
Explorateur de fichiers
Fig. 1.5 Explorateur de fichiers

Avant d'exécuter le code, voici quelques remarques :

  • Il n'y a pas obligation de terminer une instruction par un point-virgule,
  • C'est le mot-clé fun qui permet de déclarer une fonction,
  • La fonction main doit être déclarée comme acceptant un tableau de String et sans valeur de retour,
  • Remarquez également qu'en Kotlin un tableau est une classe générique (Array),
  • Également une déclaration se constitue d'abord du nom de la variable et ensuite du type,
  • La fonction println, disponible immédiatement et sans aucun import, permet d'afficher du texte à l'écran et sans aucun retour à la ligne. C'est l'équivalent de l'appel fastidieux System.out.printlnde Java,
  • Enfin, ce code n'a pas déclaré de classe : contrairement au Java où toute fonction doit être membre d'une classe. Il n'y aucun package déclaré non plus. Elle sera donc déclarée dans une classe nommée HelloKt, qui représente l'ensemble des déclarations au niveau du fichier Hello.kt. D'où la déclaration de classe principale HelloKt dans notre configuration Gradle.

Pour exécuter le code, il suffit de double-cliquer sur la tâche Gradle application→run (fig 1.6). Notre texte s'affiche donc à l'écran après compilation (fig 1.7). Notez que vous aurez peut-être besoin de basculer la vue exécution en mode texte (fig 1.7 étape 2), et que vous pourrez directement relancer l'exécution à l'aide du bouton vert (fig 1.7 étape 4).

Exécution de l'application
Fig. 1.6 Exécution de l'application
Résultat de l'exécution
Fig. 1.7 Résultat de l'exécution

II. Bases du langage

II-A. Déclarations de variables

En Kotlin, on distingue les variables dont on peut changer la valeur, et les variables immuables qui ne sont pas tout à fait des constantes :

  • le mot-clé val permet de déclarer une variable immuable,
  • le mots-clé var permet de déclarer une variable altérable.
 
Sélectionnez
val nom:String = "Toto"
// nom = "Dodo" // Interdit !!! Car nom a été déclaré avec val
var age = 10
age += 12 // aucun problème car age est altérable.

Pour rappel, il faut préciser le type de variable après le nom, et pas avant comme en Java.

Évidemment, une variable immuable doit être initialisée lors de sa déclaration.

Cela fonctionne aussi bien pour les types « primitifs » (tout est objet en Kotlin) que pour les types personnels.

 
Sélectionnez
class Personne(val nom : String, var age : Int = 10)

val jean:Personne = new Personne("Jean", 25)

Même si les classes en Kotlin seront expliquées ultérieurement, sachez qu'ici la classe Personne dispose d'une propriété nom en lecture-seule, et d'une propriété age en lecture-écriture, dont la valeur par défaut est 10. (Notez bien cependant qu'ici la classe Personne n'a pas défini de méthode modificatrice, mais si c'était le cas, il aurait aussi été probable que la variable jean puisse voir son état modifié bien qu'ayant été déclarée comme val ! Ici le mot-clé val empêche juste de changer l'objet pointé par jean.)

II-B. Types de base

Les types numériques Byte, Short, Int, Long, Float, et Double sont équivalents aux types existants en Java : si ce n'est que, pour rappel, en Kotlin, tout est objet. Ainsi on peut écrire :

 
Sélectionnez
2.toString()
10.downTo(0) // génère le range (c'est à dire l'intervalle) décroissant   de 10 à 0.

Le type Boolean fonctionne aussi comme en Java :

 
Sélectionnez
val under18 = age < 18
val isLucas = name == "Lucas"
val iFoundHim = under18 && isLucas

Le type String dispose des mêmes méthodes qu'en Java, mais aussi des méthodes supplémentaires (liste complète iciListe des méthodes de la classe String). Ainsi on peut écrire :

 
Sélectionnez
println("martin".all{it.isLowerCase()}) // Teste si le mot entier est en basse casse.

La méthode all, permettant de tester si tous les éléments d'une collection remplissent un critère, n'existe pas en Java : c'est une extension du langage Kotlin. Le morceau de code constitué par les accolades est une fonction anonyme (ou lambda) : il en sera question ultérieurement.

Kotlin ajoute aussi le type Range (intervalle), très utile.

Ainsi on peut notamment exécuter une boucle grâce à un objet Range (en effet, la syntaxe basique Java pour effectuer une boucle n'existe pas en Kotlin) :

 
Sélectionnez
for (i in 3..7){
    println(i)
} // Affiche les chiffres de 3 à 7 sur plusieurs lignes.

Mais on peut aussi obtenir une intervalle décroissant avec downTo :

 
Sélectionnez
for (i in 10 downTo 0) {
    println(i)
} // Compte à rebours de 10 à 0, sur plusieurs lignes.

Il est aussi possible de préciser une progression autre que 1 :

 
Sélectionnez
for (i in 2..36 step 3) println(i) // Affiche les nombres 2,5,8,…,35

Il est aussi possible de combiner un intervalle décroissant et une progression définie :

 
Sélectionnez
for (i in 36 downTo 2 step 3) println(i) // Affiche les nombres 36,33,…,3

Nous reviendrons plus tard dans ce chapitre sur la boucle for.

Sachez aussi, comme nous le verrons plus tard avec les fonctions, le type void n'existe pas en Kotlin, mais on utilise à la place le type Unit (qui ne s'applique pas qu'aux fonctions).

N'oubliez pas que vous pouvez vous rendre compte des méthodes disponibles pour un objet donné dans le playground Kotlin ou dans l'IDE Intellij Idea grâce à la commande CTRL+ESPACE après avoir saisi le point accolé à l'objet.

Nous reviendrons plus loin sur les tableaux (array) ainsi que sur les tables d'associations (map) dans la prochaine section.

II-C. Les collections natives

En Kotlin, il n'y a pas besoin d'importer quoi que ce soit pour utiliser les collections. De plus, grâce au mécanisme de fonctions d'extension (que nous verrons ultérieurement lors du prochain chapitre sur les classes), les collections bénéficient de plusieurs méthodes supplémentaires par rapport à leurs équivalents Java.

II-C-1. Les tableaux

Tout d'abord, en Kotlin, les tableaux sont représentés par la classe générique Array : ainsi l'on peut déclarer par exemple, un Array<Int>, un Array<String>, voire même un Array<Personne> (type personnel défini peu avant dans ce chapitre). Evidement, un Array ne peut contenir que des valeurs d'un même type commun.

On peut construire un Array de deux manières différentes : soit par le constructeur Array, soit par la fonction arrayOf. La fonction arrayOf est plus proche de ce que l'on connaît en Java, tandis que le constructeur Array permet de bénéficier de plus de contrôle sur l'initialisation :

 
Sélectionnez
val tableau1 = arrayOf(2,10,-1,4,9)
val tableau2 = Array(10, { i -> i * 2 }) // utilisation d'une fonction lambda (fonction anonyme)
assert(tableau1[1] == 10) // On accède aux éléments comme en Java
tableau1[0] = 170
assert(tableau1[0] == 170) // De même pour la modification

Ici j'utilise notamment l'instruction assert, dans un but purement illustratif : si l'expression contenue en son appel est fausse, une exception est lancée, sinon, il ne se passe rien de nouveau. Ainsi c'est un simple moyen d'illustrer la valeur de la variable ainsi testée au lecteur.

Plusieurs remarques au sujet de l'initialisation via le constructeur :

  • le premier paramètre décrit le nombre d'éléments du tableau
  • le deuxième est une fonction qui prend l'index de l'élément à initialiser et qui retourne une valeur. Bien qu'ici j'ai utilisé une fonction anonyme pour la clarté du code, je pouvais aussi bien utiliser une fonction régulière. Même si nous verrons les fonctions ultérieurement, vous pouvez également remarquer qu'il s'agit d'une situation où l'on passe une fonction à une fonction (ici, la fonction anonyme au constructeur).

Rien ne nous empêche de déclarer des tableau multi-dimensionnels, que ce soit par le biais du constructeur ou de la fonction arrayOf :

 
Sélectionnez
val tableau1 = arrayOf(arrayOf(1,2,3), arrayOf(4,5))
val tableau2 = Array(3, {i -> Array(3, {j -> i*j})})

Évidemment, les tableaux gardent une taille fixe.

Les tableaux disposent aussi de fonctions supplémentaires par rapport à leur version Java. Parmi la multitude de fonctionsla multitude de fonctions supplémentaires pour Array, citons :

 
Sélectionnez
val tab = arrayOf(7,10,15,3,6,9,12)
tab.sum()
tab.sort()
tab.sorted() // idem que sort() mais se contente de retourner le résultat au lieu de modifier le tableau contenu dans tab
tab.reverse()
tab.reversed()
tab.min()
tab.max()
tab.first()
tab.last()
tab.take(3) // les 3 premiers éléments du tableau sans le modifier
tab.drop(3) // le tableau sans ses 3 premiers éléments sans le modifier

II-C-2. Les listes

II-D. Interpolation de chaînes

Cette fonctionnalité nous permet de concaténer des chaînes plus facilement. Si en java nous voulons écrire une séquence du type :

 
Sélectionnez
int a = 10;
int b = 6;
String c = "abc";
System.out.println("[" + a + ", " + b*2 + ", " + c + "]");

En Kotlin on pourra simplement écrire :

 
Sélectionnez
val a = 10
val b = 6
val c = "abc"
println("[$a, ${b*2}, $c]")

Ainsi :

  • On utilise qu'une seule chaîne de caractères,
  • $a permet de substituer la valeur de la variable a dans la chaîne, quel que soit son type (souvenez-vous qu'en Kotlin, tout est objet),
  • ${b*2} s'écrit avec accolades étant donné qu'il s'agit d'une expression, et non plus d'une simple référence à une variable.

II-E. Inférence de type

Dans de nombreuses situations, le compilateur Kotlin est capable de reconnaître le type d'une variable grâce à l'expression qui a servi à l'initialiser :

 
Sélectionnez
val age = 10 // age est déclaré comme Int
val nom = "Toto" // nom est déclaré comme String
var prix = 10.2 // prix est déclaré comme Double

Il va de soi que pour une variable non initialisée de suite (donc de type var), il faut en préciser le type : l'inférence de type ne pouvant s'appliquer dans ce cas.

De même l'inférence de type fonctionne aussi avec les types complexes : qu'ils soient définis par Kotlin ou par nous-même.

 
Sélectionnez
class Personne(val name: String)
val jean = Personne("Jean") // jean est de type Personne.

Nous verrons par la suite d'autre inférence de type.

II-F. Expressions

Toute instruction qui retourne une valeur constitue une expression : simple valeur littérale (3, « Toto »…), expression arithmétique ou booléenne, appel de fonction (même une fonction ne retournant aucune valeur exploitable : c'est-à-dire une fonction définie comme retournant Unit), déclaration d'une fonction anonyme… Ces expressions peuvent donc être utilisées pour initialiser des variables.

 
Sélectionnez
val message = if (age < 18) "C'est un mineur !" else "Il est majeur." // Se référer à la section suivante sur les structures de contrôle.

Certaines instructions ne retournent jamais de valeur : c'est le cas pour la boucle while, comme nous le verrons dans une prochaine section du chapitre :

 
Sélectionnez
val resultat = while(i < 10) {i += 1} // Strictement interdit !!!

Par ailleurs les affectations de variables ne sont pas des expressions : il est donc impossible de les enchaîner au sein d'une même instruction :

 
Sélectionnez
var b = 0
val a = b = 10 // Strictement interdit !!!

II-G. Tuples et déconstruction

Kotlin dispose d'un support pour les tuples simples que sont les Paires (Pair) et Triples (Triple). Si un tableau ne peut contenir que des valeurs de types communs, les tuples permettent d'agréger plusieurs types de données : en Kotlin on peut simplement utiliser les Pair et Triple pour combiner respectivement deux ou trois éléments. On accède alors simplement aux différentes composantes par l'intermédiaire des méthodes component1(), component2(), component3(), où component1() retourne la 1ère composante du tuple.

 
Sélectionnez
val tuple1 = Pair('a', 25) // de type Pair<Char, Int>
val valeur1_1 = tuple1.component1()
val valeur1_2 = tuple1,component2()
assert(valeur1_1 == 'a')
assert(valeur1_2 == 25)

val tuple2 = Triple('a', 10, "Toto") // de type Triple<Char, Int, String>
val valeur2_1 = tuple2.component1()
val valeur2_2 = tuple2.component2()
val valeur2_3 = tuple2.component3()
assert(valeur2_1 == 'a')
assert(valeur2_2 == 10)
assert(valeur2_3 == "Toto")

Nous pouvons aussi accéder aux différentes composantes d'un tuple par le biais de ce que l'on appelle une déconstruction (destructuring) :

 
Sélectionnez
val monTuple = Triple('a', 10, "Toto")
val (a, b, c) = monTuple // déconstruction !
assert(a == 'a')
assert(b == 10)
assert(c == "Toto")

La déconstruction a ici lieu parce que l'on tente ici d'affecter, non pas une simple variable, mais une structure qui dispose de plusieurs variables (ici la structure (a,b,c)) à une structure qui a la même forme (soit ici une agrégation de trois valeurs). Remarquez qu'il est interdit de déclarer val Triple(a,b,c) = Triple('a', 10, "Toto") : il faut simplement décrire la forme de la structure à gauche de l'affectation.

Sachez également qu'il est possible d'ignorer certaines valeurs lors de la déconstruction :

 
Sélectionnez
val (a, _ , c) = Triple('a', 10, "Toto")
assert (a == 'a')
assert (c == "Toto")

II-H. Structures de contrôle

Les structures de contrôle sont similaires à celles du java (if, for, while, switch, ...), à ceci près que la plupart des structures de Kotlin sont des expressions, et donc leur résultat peuvent être affectés à des variables, telles que nous l'avons vu dans la section précédente ; mais également, l'équivalent de la structure switch en Kotlin est beaucoup plus simple d'utilisation et beaucoup plus puissante.

II-H-1. L'instruction while

L'instruction while (ou do ... while) en Kotlin, tout comme en Java, ne retourne pas d'expression. Elle s'utilise aussi comme en Java : rien de nouveau.

 
Sélectionnez
var i = 0
while (i < 10) {
  i += 1
  println(i)
}

var j = 0
do {
   j += 1
   println(j)
} while (j < 10)

Les mots-clés break et continue fonctionnent comme en Java.

II-H-2. L'instruction for

L'utilisation de l'instruction for, telle que l'on connaît en Java (initialisation/condition/mise à jour accompagnée d'un bloc d'instructions) est strictement interdite en Kotlin. En effet, on ne peut utiliser la boucle for que sur des objets « iterable » : traditionnellement des ranges ou certains types de collections. Par « iterable » je veux simplement parler de types où l'on puisse obtenir les différentes valeurs d'une variable de ce type par appels successifs à la méthode d'extraction (appelons-là next() pour mieux illustrer).

Ce qui peut par exemple donner :

 
Sélectionnez
// i n'existe pas dans cette portée
for (i in 0..10) println(i)

Remarquez-bien ici qu'il ne faut utiliser ni le mot-clé val, ni var, afin de déclarer la variable i : qu'elle existe déjà ou non, il ne faut surtout pas les utiliser.

Un autre exemple avec simple tableau :

 
Sélectionnez
val monTableau = arrayOf(2,3,5,7,11,13)
for (premier in monTableau) {
    println("$premier est un nombre premier")
}

Remarquez aussi que je peux m'affranchir des accolades si je n'ai besoin que d'une simple expression pour la boucle for (ce qui est donc possible dans ce deuxième exemple).

Enfin, si l'on souhaite parcourir un tableau tout en conservant les valeurs des différents index, on peut utilise la méthode withIndex() mais avec une syntaxe un peu particulière pour les variables d'index et de valeur :

 
Sélectionnez
val monTableau = arrayOf(2,3,5,7,11,13)
for ((index, valeur) in monTableau.withIndex()) {
   println("$index: $valeur")
}

Remarquez que la méthode withIndex() retourne une Pair où la première valeur est l'index, donc il faut empaqueter les variables d'index et de valeur entre parenthèses afin de procéder au mécanisme de déconstruction. Mais rien ne nous empêche de procéder en deux étapes :

 
Sélectionnez
val monTableau = arrayOf(2,3,5,7,11,13)
for (tupleCourant in monTableau.withIndex()) {
    val index = tupleCourant.component1()
    val valeur = tupleCourant.component2()
    println("$index: $valeur")
}

Enfin, tout comme pour les boucles while (do...while), les mots-clés break et continue fonctionnent comme en Java.

Vous avez aimé ce tutoriel ? Alors partagez-le en cliquant sur les boutons suivants : Viadeo Twitter Facebook Share on Google+   

  

Les sources présentées sur cette page sont libres de droits et vous pouvez les utiliser à votre convenance. Par contre, la page de présentation constitue une œuvre intellectuelle protégée par les droits d'auteur. Copyright © 2018 Laurent Bernabé. Aucune reproduction, même partielle, ne peut être faite de ce site et de l'ensemble de son contenu : textes, documents, images, etc. sans l'autorisation expresse de l'auteur. Sinon vous encourez selon la loi jusqu'à trois ans de prison et jusqu'à 300 000 € de dommages et intérêts.