Dis­po­nible depuis 2016 seulement (avec la version 1.0), mais déjà très apprécié, Kotlin est une al­ter­na­tive à Java. Ce langage de pro­gram­ma­tion orienté objet a été développé par JetBrains, une en­tre­prise tchèque de dé­ve­lop­pe­ment de logiciels. Il séduit de nombreux dé­ve­lop­peurs par son caractère léger, mais aussi par la faible sur­ve­nance d’erreurs d’exécution, en par­ti­cu­lier les erreurs Null­Poin­te­rEx­cep­tions très redoutées. Kotlin est tout par­ti­cu­liè­re­ment apprécié pour le dé­ve­lop­pe­ment d’ap­pli­ca­tions Android, mais rencontre également un immense succès comme base de départ pour des ap­pli­ca­tions Ja­vaS­cript.

Kotlin s’inspire fortement de Java : ces deux langages de pro­gram­ma­tion ne sont certes pas com­pa­tibles, mais Kotlin peut être trans­formé en bytecode lisible par une machine virtuelle Java (JVM).

In­tro­duc­tion à Kotlin avec des exemples

Pour démarrer avec Kotlin, vous pouvez té­lé­char­ger ce com­pi­la­teur sur le site Internet officiel. L’ins­tal­la­tion est facilitée en cas d’uti­li­sa­tion d’un en­vi­ron­ne­ment de dé­ve­lop­pe­ment (IDE) : IntelliJ IDEA (également développé par JetBrains), Eclipse (avec le plugin cor­res­pon­dant), NetBeans et Android Studio peuvent par exemple supporter Kotlin.

Conseil

Les fa­bri­cants de Kotlin mettent à dis­po­si­tion une sandbox en ligne, dans laquelle vous pourrez tester tous les exemples.

Packages

Au début d’un projet, vous importez les packages dont vous avez besoin pour le réaliser. Vous dé­fi­nis­sez également le package sur lequel vous tra­vail­lez ac­tuel­le­ment. Les packages con­tien­nent des classes et des fonctions.

package test.bar
import foo.bar
import footoo.bar as footoo

Afin d’éviter tout problème de noms iden­tiques, Kotlin vous permet de renommer les packages avec as. L’at­tri­bu­tion d’un alias aux packages n’impose pas de suivre la structure du ré­per­toire dans lequel ils se trouvent. Il est toutefois re­com­mandé de procéder de cette façon pour permettre une vue d’ensemble.

Note

Kotlin charge au­to­ma­ti­que­ment les prin­ci­paux packages dans chaque projet.

Con­trai­re­ment à Java, Kotlin vous permet également d’importer des fonctions in­di­vi­duelles depuis d’autres packages. Pour ce faire, on indique le chemin correct :

import foo.bar.myFunction

La fonction peut ensuite être utilisée tout à fait nor­ma­le­ment.

Conseil

Dans Kotlin, il n’est pas obli­ga­toire de terminer les lignes de code par un marqueur, par exemple un point-virgule.

Variables

Kotlin connaît deux types de variables : les variables immuables, qui sont en lecture seule, sont in­tro­duites par val. Les autres variables, dont la valeur est mo­di­fiable au fil du programme, sont in­tro­duites par var.

val name = "Clara Oswald"
var age = 22

Con­trai­re­ment au nom, qui est fixe, l’âge peut être adapté, par exemple dans une fonction.

Note

Dans cet exemple, Kotlin a déterminé seul le type de valeur des variables. Il est également possible d’indiquer in­di­vi­duel­le­ment ces types de base.

Types de base (basic types)

Kotlin travaille avec certains types de variables et de classes. Chaque type est un objet, ce qui distingue quelque peu Kotlin de Java. Alors que les langages de pro­gram­ma­tion plus anciens doivent tout d’abord placer les types primitifs dans un wrapper pour qu’ils se com­por­tent comme des objets, avec Kotlin, c’est inutile puisque tous les types sont d’ores et déjà des objets.

Nombres (numbers)

Dans Kotlin, vous pouvez utiliser des nombres sans aucune balise : le com­pi­la­teur comprend que ce sont des valeurs nu­mé­riques. Les virgules sont réalisées à l’aide de points. Il est également possible d’utiliser des nombres hexa­dé­ci­maux. Afin de permettre une meilleure li­si­bi­lité, les sé­pa­ra­teurs de milliers sont re­pré­sen­tés à l’aide de tirets. Kotlin connaît dif­fé­rents types de nombres pouvant tous avoir une taille maximale dif­fé­rente :

  • Long : 64 bits
  • Int : 32 bits
  • Short : 16 bits
  • Byte : 8 bits
  • Double : 64 bits
  • Float : 32 bits

Pour Double et Float, il s’agit de nombres flottants qui ne se com­por­tent pas comme les nombres fixes dans des calculs complexes. Comme pour tous les types, vous pouvez indiquer des nombres de façon concrète dans votre code.

val myNumber: Long = 40

Il est possible de convertir un nombre d’un type en nombre d’un autre type.

val myInt = 600
val myLong= myInt.toLong()

La commande toLong convertit la valeur « Int » en valeur « Long ». La commande fonc­tionne de façon analogue pour les autres types de nombres.

String

Un string est un ensemble de mots ou des phrases complètes, autrement dit, une chaîne de ca­rac­tères. Pour utiliser un string dans Kotlin, placez le texte entre des guil­le­mets doubles. Si vous souhaitez intégrer plusieurs lignes de texte, il est né­ces­saire d’ajouter trois guil­le­mets doubles au début et à la fin (raw string).

val myString = "Ce string comporte une seule ligne."
val myLongString = """Ce string s'étend
sur plusieurs lignes."""

Comme dans de nombreux langages de pro­gram­ma­tion, Kotlin permet l’uti­li­sa­tion de ca­rac­tères d’échap­pe­ment : une barre oblique inversée permet de désigner un caractère ne faisant pas partie du string et devant être traité comme un caractère de contrôle. À l’inverse, une barre oblique inversée permet également d’insérer dans le string des ca­rac­tères ayant nor­ma­le­ment une autre sig­ni­fi­ca­tion dans Kotlin. Les ca­rac­tères d’échap­pe­ment suivants sont possibles :

  • \t : ta­bu­la­tion
  • \b : retour arrière
  • \n : nouvelle ligne
  • \r : retour chariot
  • \' : guil­le­mets simples
  • \" : guil­le­mets doubles
  • \\ : barre oblique inversée
  • \$ : symbole dollar

Dans les strings, le symbole dollar sert à indiquer une balise. Il est possible de la définir comme variable lors d’une étape préalable. La balise est alors remplacée par une véritable valeur dans l’édition.

val author = "Sandra"
val myString = "Ce texte a été écrit par $author"

Ca­rac­tères (cha­rac­ters)

Pour certains ca­rac­tères, Kotlin met également à dis­po­si­tion le type de données spé­ci­fique Character. Plutôt que de l’indiquer entre des guil­le­mets doubles, on utilise des guil­le­mets simples.

var model = 'A'

Boolean

Le type de base Boolean traduit une valeur de vérité qui peut être définie sur vrai (true) ou faux (false).

Arrays Kotlin

Dans Kotlin, un array est une col­lec­tion de données. Vous pouvez cons­truire un array avec arrayOf() ou Array(). La première de ces fonctions est simple :

val myArray1 = arrayOf(0, 1, 2, 3, 4, 5)

On génère ainsi un array avec des chiffres de 1 à 5. Ces col­lec­tions peuvent toutefois abriter d’autres types, comme des strings et des booleans, voire un mélange des deux. Si on souhaite limiter l’array à un type, il suffit de l’indiquer dans la fonction.

val myArray2 = arrayOf<int>(10, 20, 30)</int>
val myArray3 = booleanArrayOf(true, true, false)

Le cons­truc­tor Kotlin Array() est plus complexe : vous devez également indiquer la longueur et une fonction lambda.

val myArray4 = Array(6, { i -> i })

Le cons­truc­tor Kotlin génère un array avec six chiffres en com­men­çant par zéro : 0, 1, 2, 3, 4, 5.

Note

Vous en ap­pren­drez davantage sur les cons­truc­tors et les fonctions lambda dans la suite de l’article.

Chaque entrée d’un array Kotlin est indexée et peut être appelée à l’aide de cet index. On utilise ici des crochets dans lesquels l’em­pla­ce­ment com­por­tant l’entrée dans la liste est indiqué.

fun main() {
	val myArray5 = arrayOf("Jan", "Maria", "Samuel")
	println(myArray5[2])
}
Note

La fonction indiquera dans ce cas « Samuel » puisque l’énu­mé­ra­tion commence par 0.

Opé­ra­teurs

Comme dans de nombreux autres langages de pro­gram­ma­tion, Kotlin utilise dif­fé­rents opé­ra­teurs pouvant être intégrés au code source. On trouve notamment les opé­ra­teurs arith­mé­tiques (+, -, *, /, %), les opé­ra­teurs de com­pa­rai­son (<, >, <=, >=, ==, !=) et les opé­ra­teurs logiques (&&, ||, !). Les mots-clés sont étroi­te­ment liés aux opé­ra­teurs. Il s’agit de termes ayant une sig­ni­fi­ca­tion fixe dans Kotlin et ne pouvant pas être réin­ter­pré­tés.

Conseil

Une liste ex­haus­tive de tous les mots-clés et opé­ra­teurs est dis­po­nible dans la do­cu­men­ta­tion of­fi­cielle de Kotlin.

In­ter­valles (ranges)

Dans Kotlin, un Range est un type qui va d’un point à un autre. Pour générer un in­ter­valle de ce type, on utilise l’opérateur .. ou les fonctions rangeTo() ou downTo(). La variante avec deux points permet un incrément. Les deux fonctions vous per­met­tent en revanche de définir un sens.

val range1 = 1..5
val range2 = 1.rangeTo(5)
val range3 = 5.downTo(1)

Dans ces deux variantes simples, vous générez un in­ter­valle avec un incrément de un. Pour modifier l’incrément, ajoutez la fonction step.

val range4 = 0..10 step(2)

Pour traiter des données in­di­vi­duelles de l’in­ter­valle, utilisez l’opérateur in. Vous pouvez par exemple générer une requête ou une boucle. Afin de vérifier si une valeur ne fait pas partie de l’in­ter­valle, on utilise l’opérateur !in.

val range5 = 0..10
fun main() {
	for (n in range5) {
		println(n)
	}
	if (7 in range5) {
		println("yes")
	}
	if (12 !in range5) {
		println("no")
	}
}
Note

Vous en ap­pren­drez davantage sur les fonctions, les boucles et les requêtes dans la suite de l’article.

Fonctions (functions) Kotlin

Les fonctions Kotlin sont toujours créées avec la commande fun. Vous dé­fi­nis­sez ensuite le nom de la fonction, ses arguments et enfin, son effet.

fun div(a: Int, b: Int): Int {
	return a/b
}
fun main() {
	println(div(100, 2))
}

Nous dé­fi­nis­sons dans un premier temps la fonction div (pour division) avec deux pa­ra­mètres Int a et b. Cette fonction doit également nous donner le résultat de la division de a par b sous la forme d’une variable Int. Fi­na­le­ment, dans la fonction main, nous appelons la fonction définie pré­cé­dem­ment, lui donnons des valeurs concrètes et affichons le résultat à l’aide de println (print line) dans la console. Kotlin exécute au­to­ma­ti­que­ment le contenu de main(). Cette fonction constitue le point de départ d’un programme Kotlin.

Remarque

Kotlin n’accepte aucune commande en dehors des fonctions. Seules les dé­cla­ra­tions y sont au­to­ri­sées.

Dans Kotlin, les fonctions com­por­tant une seule ligne de code sont fa­ci­le­ment re­pré­sen­tées. Plutôt que d’ouvrir une accolade, d’écrire une nouvelle ligne in­ter­ca­lée et de refermer l’accolade, on utilise le symbole égal. Il est ainsi possible de se passer de la commande return.

fun div(a: Int, b: Int): Int = a/b
fun main() = println(div(100, 2))

Afin d’éviter une erreur due à des pa­ra­mètres erronés, vous pouvez indiquer des valeurs standard lors de la dé­fi­ni­tion de la fonction. Si les pa­ra­mètres sont laissés libres lors de l’appel de la fonction, les valeurs par défaut seront utilisées.

fun div(a: Int = 10, b: Int = 5): Int = a/b
fun main() = println(div())

Lambdas

Une fonction lambda (ou fonction anonyme) est une fonction qui n’ap­par­tient ni à une classe ni à un objet. Les fonctions lambda sont placées di­rec­te­ment dans d’autres fonctions ou variables. Elles sont appelées sans utiliser le mot-clé fun. En principe, les fonctions lambda s’utilisent comme les variables de type val et sont également générées de cette façon.

fun main() {
	val myMessage = { println("Coucou tout le monde !") }
	myMessage()
}

Dans Kotlin, les ex­pres­sions lambda doivent toujours être placées dans des accolades et peuvent également traiter des arguments de fonction. Ces derniers sont iden­ti­fiés par une flèche qui sépare les pa­ra­mètres du noyau de l’ex­pres­sion.

fun main() {
    val div = {a: Int, b: Int -> a/b}
    println(div(6,2))
}

Classes

À l’instar de Java, les classes de Kotlin sont des col­lec­tions de données et de fonctions. Pour définir une classe, il suffit d’utiliser le mot-clé class et de compléter les in­for­ma­tions de la nouvelle classe.

class Tardis {
	var year: Int
	var place: String
	constructor(year: Int, place: String) {
		this.year = year
		this.place = place
	}
}

Dans Kotlin, le cons­truc­tor est une fonction utilisée pour créer des objets. Pour y parvenir, le langage de pro­gram­ma­tion utilise des primary et des secondary cons­truc­tors (cons­truc­teurs primaires et se­con­daires). Les primary cons­truc­tors sont une syntaxe abrégée pratique alors que les secondary cons­truc­tors sont une forme de syntaxe similaire à celle de nombreux autres langages orientés objet dont fait notamment partie Java. Cette seconde variante est celle présentée dans l’exemple ci-dessus.

Il est toutefois possible de faire l’impasse sur le secondary cons­truc­tor et d’utiliser plutôt un primary cons­truc­tor. Écrivez ce cons­truc­teur di­rec­te­ment dans la ligne d’en-tête de la classe et indiquez également les pa­ra­mètres de la classe. Le nombre de lignes de code s’en trouve nettement réduit.

class Tardis constructor(var year: Int, var place: String)

Si vous ne souhaitez pas apporter d’in­for­ma­tions com­plé­men­taires sur la vi­si­bi­lité de la classe (public, private, protected), vous pouvez tout à fait vous passer en­tiè­re­ment du mot-clé.

class Tardis (var year: Int, var place: String)

Ces trois exemples de code génèrent le même résultat.

Vous pouvez à présent utiliser cette classe dans le reste de votre code source et le compléter avec des valeurs concrètes.

val tardis1 = Tardis(2133, "Dunlop Station")
val tardis2 = Tardis(1885, "Northhampton")

Comme dans la plupart des langages orientés objet, vous pouvez également accéder di­rec­te­ment aux pro­prié­tés et aux méthodes d’un objet en plaçant un point et le nom de la propriété ou de la méthode derrière l’objet.

class Tardis (var year: Int, var place: String)
val tardis1 = Tardis(2133, "Dunlop Station")
val tardis2 = Tardis(1885, "Northhampton")
fun main() {
    println(tardis1.year)
}

La data class constitue une par­ti­cu­la­rité de Kotlin. Ce type de classe est conçu pour en­re­gis­trer uni­que­ment des données. En principe, il suffit pour cela d’une ligne de code.

data class User (var username: String, var name: String, var age: Int)

Cette classe peut être utilisée di­rec­te­ment.

data class User (var username: String, var name: String, var age: Int)
fun main() {
    val user1 = User ("River Song", "Melody Pond", 200)
    println("Username: " + user1.username)
    println("Name: " + user1.name)
    println("Age: " + user1.age)
}

Objets (objects)

Dans Kotlin, les objets sont des instances qui ne peuvent être définies qu’une seule fois de cette façon (singleton). Ces objets con­tien­nent gé­né­ra­le­ment des variables et des fonctions. Ils sont en principe créés de façon similaire aux classes, c’est-à-dire avec une seule ligne de code. À sa création, l’objet est vide. Son contenu est indiqué dans le corps de l’objet.

object myObject {
	fun sum(a: Int, b: Int): Int {
		return a+b
	}
}

Boucles (loops)

Dans Kotlin, trois types de boucles sont dis­po­nibles : while, do..while et if. Elles se com­por­tent comme leurs équi­va­lents dans les autres langages de pro­gram­ma­tion. Une boucle while se poursuit jusqu’à ce qu’une condition dé­ter­mi­née survienne.

fun main() {
    var n = 1
    while (n <= 10) {
        println(n++)
    }
}

La boucle do..while se comporte de façon similaire à la boucle while avec pour dif­fé­rence que le contenu de la boucle est parcouru au minimum une fois puisque la vé­ri­fi­ca­tion a lieu uni­que­ment à la fin.

fun main() {
    var n = 1
    do {
        n++
    }	
    while (n < 1)
    println(n)
}

La boucle for se poursuit tant qu’une condition reste valable.

val myRange = 0..10
fun main() {
	for (n in myRange) {
		print("$n ")
	}
}

Con­di­tions

Kotlin offre trois pos­si­bi­li­tés pour appliquer certaines ins­truc­tions ou ra­mi­fi­ca­tions : if, if..else et when. La requête if permet à l’or­di­na­teur de réaliser une tâche lorsque la condition est sa­tis­faite.

val whoCompanion = arrayOf("Bill Potts", "Clara Oswald", "Amy Pond", "Martha Jones", "Donna Noble", "Rose Tyler")
fun main() {
    if ("Rose Tyler" in whoCompanion) {
        print("yes")
    }
}

else permet d’ajouter une action qui sera exécutée lorsque la condition n’est pas sa­tis­faite.

val whoCompanions9 = arrayOf("Rose Tyler")
val whoCompanions10 = arrayOf("Martha Jones", "Donna Noble", "Rose Tyler")
val whoCompanions11 = arrayOf("Clara Oswald", "Amy Pond")
val whoCompanions12 = arrayOf("Bill Potts", "Clara Oswald")
fun main() {
    var whoCompanion = "Clara Oswald"
    if (whoCompanion in whoCompanions9) {
        print("yes")
    }
    else {
        print("no")
    }
}

Fi­na­le­ment, l’ex­pres­sion when est une spé­ci­fi­cité de Kotlin : des actions dif­fé­rentes sont réalisées en fonction des dif­fé­rents états. L’effet de l’ex­pres­sion when est ainsi similaire à ce celui de l’ex­pres­sion switch dans d’autres langages de pro­gram­ma­tion, mais il permet davantage de précision.

Placez les dif­fé­rents cas de vé­ri­fi­ca­tion dans le corps de when. Ils sont toujours associés à une variable définie.

var age = 17
fun main() {
    when {
     	age > 18 -> println("Tu es trop vieux !")
     	age == 18 -> println("Déjà adulte !")
     	age == 17 -> println("Bienvenue !")
     	age <= 16 -> println("Tu es trop jeune !")
    }
}

L’argument peut toutefois être transmis di­rec­te­ment à when et ne doit pas être répété à chaque fois dans le corps. Par ailleurs, une condition unique peut dé­clen­cher plusieurs actions. Pour cela, créez un nouveau corps avec des accolades. L’ex­pres­sion else vous aidera à exclure les cas imprévus.

fun multi(a: Int, b: Int, c: Int): Int {
    return a*b*c
}
fun main() {
    val d = "yes"
    when (d) {
        "no" -> println("Aucun calcul")
        "yes" -> {
            println("Démarrer le calcul") 
            println(multi(5, 2, 100))
            println("Calcul terminé")
        }
    else -> println("Saisie erronée")    
    }
}

Nul­la­bi­lity

L’erreur Null­Poin­te­rEx­cep­tion est une source de problèmes im­por­tante dans le cas d’une pro­gram­ma­tion avec Java. Cette erreur survient lorsqu’on fait référence à un objet dont la valeur est null. Kotlin contourne ce problème en refusant d’emblée que les variables prennent la valeur null. Si le cas se pré­sen­tait, le message « Null can not be a value of a non-null type String » ou un autre message cor­res­pon­dant ap­pa­raî­trait avant la com­pi­la­tion.

Il existe toutefois des si­tua­tions où l’on souhaite utiliser la valeur null à dessein. Pour y parvenir, Kotlin utilise l’opérateur safe call : ?.

fun main() {
	var password: String? = null
	print(password)
}

En indiquant ce symbole, vous précisez ex­pli­ci­te­ment à Kotlin que la valeur null est ac­cep­table. Le programme indiquera alors null. Toutefois, si vous souhaitez traiter une propriété spé­ci­fique de cette variable, vous devrez de nouveau utiliser l’opérateur safe call.

fun main() {
	var password: String? = null
	print(password?.length)
}

Ce code produira à nouveau la valeur null, mais aucune erreur et le programme fonc­tion­nera. Il est toutefois plus élégant d’indiquer une valeur al­ter­na­tive. Pour cela, utilisez l’opérateur Elvis ?: ainsi nommé en raison de la banane que décrit le smiley formé par cette séquence de ca­rac­tères.

fun main() {
    val firstName = null
    val lastName = "Pond"
	val name: String = firstName?: "Prénom manquant" + " " + lastName?: "Nom manquant"
	print(name)
}

Dans cet exemple, des in­for­ma­tions seront affichées lorsqu’une variable aura la valeur null.

Aller au menu principal