© Dunod, 2020

ISBN : 9782100818976

Traduction autorisée depuis l’édition en anglais de l’ouvrage Kotlin Programming : The Big Nerd Ranch Guide (1re édition) par Josh Skeen et David Greenhalgh, publié par Pearson Education, Inc. en 2019 dans la collection des Big Nerd Ranch Guides.

Copyright © 2018 Big Nerd Ranch LLC.

All rights reserved. No part of this book may be reproduced or transmitted in any form or by any means, electronic or mechanical, including photocopying, recording or by any information storage retrieval system, without Permission from Pearson Education, Inc.

Image de couverture : © batman6794-adobestock

La photo de la couverture représente l’une des fortifications de l’île de Kotline.
Cette île est située en mer Baltique, à l’entrée de la baie de la Néva, à une vingtaine de kilomètres de Saint-Pétersbourg.
Son extrémité orientale abrite la ville et le port fortifiés de Kronstadt.
L’île de Kotline a donné son nom au langage Kotlin qui a été créé par une équipe de développeurs de JetBrains à Saint-Pétersbourg.

www.dunod.com
Dunod Editeur
11 rue Paul Bert, 92240 Malakoff

Le code de la propriété intellectuelle n'autorisant, aux termes des paragraphes 2 et 3 de l'article L122-5, d'une part, que les « copies ou reproductions strictement réservées à l'usage privé du copiste et non destinées à une utilisation collective » et, d'autre part, sous réserve du nom de l'auteur et de la source, que « les analyses et les courtes citations justifiées par le caractère critique, polémique, pédagogique, scientifique ou d'information », toute représentation ou reproduction intégrale ou partielle, faite sans consentement de l'auteur ou de ses ayants droit, est illicite (art; L122-4). Toute représentation ou reproduction, par quelque procédé que ce soit, notamment par téléchargement ou sortie imprimante, constituera donc une contrefaçon sanctionnée par les articles L 335-2 et suivants du code de la propriété intellectuelle.

All rights reserved. This work may not be translated or copied in whole or in part without the written permission of the publisher.

Toute reproduction d'un extrait quelconque de ce livre par quelque procédé que ce soit, et notamment par photocopie ou microfilm, est interdite sans autorisation écrite de l'éditeur.

Table des matières

Avant-propos11

1.  Présentation de Kotlin11

2.  Remerciements14

Chapitre 1. Votre première application Kotlin17

1.  Installation d'IntelliJ IDEA17

2.  Votre premier projet Kotlin18

3.  Kotlin REPL28

4.  Pour les plus curieux : pourquoi utiliser IntelliJ ?29

5.  Pour les plus curieux : choix du support de la JVM32

6.  Défi : test des opérateurs arithmétiques dans REPL34

Chapitre 2. Variables, constantes, et types35

1.  Types35

2.  Déclaration d’une variable36

3.  Types prédéfinis de Kotlin39

4.  Variables en lecture seule39

5.  Inférence de type44

6.  Constantes de compilation46

7.  Inspection du bytecode Kotlin47

8.  Pour les plus curieux : types primitifs Java en Kotlin51

9.  Défi : acquisition d’une monture52

10.  Défi : la corne de la licorne52

11.  Défi : le miroir magique53

Chapitre 3. Conditions55

1.  Instruction If/else55

2.  Plages69

3.  Expressions when70

4.  Modèles de chaînes73

5.  Défi : test des plages74

6.  Défi : amélioration de l’Aura75

7.  Défi : format de l’état configurable76

Chapitre 4. Fonctions77

1.  Extraction de code dans des fonctions77

2.  Anatomie d’une fonction81

3.  Appel d’une fonction85

4.  Refactoring des fonctions86

5.  Écriture de vos propres fonctions88

6.  Arguments par défaut90

7.  Fonctions à expression unique91

8.  Fonctions Unit92

9.  Arguments de fonction nommés93

10.  Pour les plus curieux : le type Nothing94

11.  Pour les plus curieux : fonctions de niveau fichier en Java95

12.  Pour les plus curieux : surcharge de fonctions96

13.  Pour les plus curieux : noms de fonction entre guillemets obliques98

14.  Défi : fonctions à expression unique99

15.  Défi : niveau d’ébriété au Fireball99

16.  Défi : état d’ébriété99

Chapitre 5. Fonctions anonymes et type de fonction101

1.  Fonctions anonymes101

2.  Prise en charge de l’inférence de type107

3.  Définition d’une fonction qui accepte une fonction109

4.  Inlining de fonction111

5.  Références des fonctions112

6.  Type de fonction en tant que type de retour114

7.  Pour les plus curieux : en Kotlin les lambdas sont des closures115

8.  Pour les plus curieux : lambdas versus classes internes anonymes116

Chapitre 6. Sécurité des références à null et exceptions119

1.  Nullabilité119

2.  Type null explicite de Kotlin121

3.  Compilation versus exécution122

4.  Sécurité de null123

5.  Exceptions130

6.  Préconditions135

7.  Null : quelle est son utilité ?138

8.  Pour les plus curieux : exceptions vérifiées versus exceptions non vérifiées139

9.  Pour les plus curieux : comment la nullabilité est-elle appliquée ?140

Chapitre 7. Chaînes de caractères143

1.  Extraction de sous-chaînes143

2.  Manipulation de chaînes148

3.  Comparaison de chaînes150

4.  Pour les plus curieux : Unicode152

5.  Pour les plus curieux : parcourir les caractères d’une chaîne153

6.  Défi : améliorer DragonSpeak153

Chapitre 8. Nombres155

1.  Types numériques155

2.  Entiers156

3.  Nombres décimaux158

4.  Conversion d’une chaîne en un type numérique158

5.  Conversion d’un Int en un Double160

6.  Mise en forme du type Double161

7.  Conversion d’un type Double en un type Int162

8.  Pour les plus curieux : manipulation des bits164

9.  Défi : calcul du nombre de pintes restantes164

10.  Défi : gestion du solde négatif164

11.  Défi : Dragoncoin164

Chapitre 9. Fonctions standard167

1.  apply167

2.  let168

3.  run169

4.  with171

5.  also171

6.  takeIf172

7.  Utilisation des fonctions standard de la bibliothèque173

Chapitre 10. Listes et sets175

1.  Listes175

2.  Itération182

3.  Lecture d’un fichier pour stocker son contenu dans une liste189

4.  Déstructuration191

5.  Ensembles (sets)191

6.  Boucles while195

7.  L’expression break198

8.  Conversion de collection198

9.  Pour les plus curieux : types Array199

10.  Pour les plus curieux : lecture seule versus immutabilité200

11.  Défi : menu de la taverne mis en forme202

12.  Défi : amélioration de la mise en forme du menu de la taverne203

Chapitre 11. Maps205

1.  Création d’une map205

2.  Accès aux valeurs d’une map207

3.  Ajout d’entrées dans une map208

4.  Modification des valeurs d’une map210

5.  Défi : le videur de la taverne215

Chapitre 12. Définition de classes217

1.  Définition d’une classe217

2.  Création des instances217

3.  Fonctions de classe218

4.  Visibilité et encapsulation220

5.  Propriétés des classes221

6.  Refactoring de NyetHack230

7.  Utilisation des packages239

8.  Pour les plus curieux : les propriétés var et val en détail240

9.  Pour les plus curieux : protection contre les conditions de course244

10.  Pour les plus curieux : package privé245

Chapitre 13. Initialisation249

1.  Constructeurs250

2.  Bloc initialisateur255

3.  Initialisation des propriétés256

4.  Ordre d’initialisation259

5.  Retardement de l’initialisation260

6.  Pour les plus curieux : déjouer les pièges de l’initialisation264

7.  Défi : l’énigme d’Excalibur267

Chapitre 14. Héritage269

1.  Définition de la classe Room269

2.  Création d’une sous-classe270

3.  Contrôle de type278

4.  Hiérarchie des types Kotlin280

5.  Pour les plus curieux : Any283

Chapitre 15. Objets285

1.  Mot-clé object285

2.  Classes imbriquées293

3.  Classes de données296

4.  Classes énumérées300

5.  Surcharge des opérateurs301

6.  Explorer le monde de NyetHack303

7.  Pour les plus curieux : définition de la comparaison structurelle307

8.  Pour les plus curieux : types de données algébriques310

9.  Défi : commande "Quitter"313

10.  Défi : implémenter une carte du monde313

11.  Défi : faire sonner la cloche313

Chapitre 16. Interfaces et classes abstraites315

1.  Définition d’une interface315

2.  Implémentation d’une interface316

3.  Implémentations par défaut319

4.  Classes abstraites319

5.  Combat dans NyetHack322

Chapitre 17. Génériques327

1.  Définition des types génériques327

2.  Fonctions génériques329

3.  Paramètres de type générique multiples330

4.  Contraintes génériques332

5.  vararg et get333

6.  in et out335

7.  Pour les plus curieux : mot-clé reified340

Chapitre 18. Extensions343

1.  Définition des fonctions d’extension343

2.  Fonctions d’extension génériques345

3.  Propriétés d’extension348

4.  Extensions sur des types nullables349

5.  Sous le capot des extensions350

6.  Emploi des extensions dans NyetHack350

7.  Définition d’un fichier d’extensions352

8.  Renommer une extension354

9.  Extensions dans la bibliothèque standard Kotlin355

10.  Pour les plus curieux : les littéraux de fonction avec des récepteurs356

11.  Défi : extension toDragonSpeak357

12.  Défi : extension du cadre357

Chapitre 19. Bases de la programmation fonctionnelle359

1.  Catégories de fonctions359

2.  Pourquoi la programmation fonctionnelle ?364

3.  Séquences365

4.  Pour les plus curieux : profilage367

5.  Pour les plus curieux : Arrow.kt368

6.  Défi : inverser les valeurs d’une Map369

7.  Défi : appliquer la programmation fonctionnelle à Tavern.kt369

8.  Défi : fenêtre glissante370

Chapitre 20. Interopérabilité Java373

1.  Interopérabilité avec une classe Java373

2.  Interopérabilité et nullité375

3.  Mappage de types378

4.  Getters, setters et interopérabilité379

5.  Au-delà des classes381

6.  Exceptions et interopérabilité390

7.  Types de fonction en Java393

Chapitre 21. Créer votre première application Android avec Kotlin397

1.  Android Studio397

2.  Définition d’une interface utilisateur404

3.  Exécution de l’application sur un émulateur406

4.  Génération d’un personnage409

5.  La classe Activity410

6.  Liaison avec les vues411

7.  Propriétés synthétiques des extensions Android de Kotlin413

8.  Définition d’un écouteur de clics416

9.  État de l’instance enregistrée417

10.  Refactoring d’une extension420

11.  Pour les plus curieux : bibliothèques Android KTX et Anko422

Chapitre 22. Introduction aux coroutines425

1.  Analyse des données des personnages425

2.  Récupération des données depuis le Web427

3.  Le thread principal Android429

4.  Activation des coroutines430

5.  Spécification d’une coroutine avec GlobalScope.async432

6.  launch vs GlobalScope.async/await434

7.  Suspension des fonctions434

8.  Défi : données en direct435

9.  Défi : force minimale435

Conclusion437

Conseils437

Marketing direct437

Merci437

Encore plus de défis439

Un nouveau projet pour améliorer vos compétences : Exercism439

Glossaire447

Index 457

Avant-propos

Pour Baker, mon petit bug préféré.

— J.S.

À Rebecca, une femme motivée, patiente, belle, qui est à l’origine de ce livre.

Pour maman et papa, qui placent l’éducation au-dessus de tout.

— D.G.

1.  Présentation de Kotlin

En 2011, JetBrains a annoncé le développement du langage de programmation Kotlin, qui constituait une alternative à l’écriture de code dans des langages comme Java ou Scala s’exécutant sur la machine virtuelle Java. Six ans plus tard, Google a annoncé que Kotlin devenait un outil de développement officiellement pris en charge par le système d’exploitation Android.

Kotlin est rapidement passé du statut de langage promis à un brillant avenir à celui de langage faisant fonctionner des applications du premier système d’exploitation mobile. Aujourd’hui, de grandes entreprises comme Google, Uber, Netflix, Capital One, Amazon, et bien d’autres encore ont adopté Kotlin à cause de ses nombreux avantages, notamment sa syntaxe concise, ses fonctionnalités modernes et son interopérabilité transparente avec le code Java hérité.

1.1.  Les raisons du succès de Kotlin

Pour comprendre l’attrait de Kotlin, vous devez d’abord comprendre le rôle de Java dans le paysage du développement logiciel moderne. Les deux langages sont étroitement liés, parce que le code Kotlin est le plus souvent écrit pour la machine virtuelle Java.

Java est un langage robuste et éprouvé qui a été l’un des langages les plus utilisés dans les bases de code de production pendant des années. Cependant, depuis la sortie de Java en 1995, les critères d’un bon langage de programmation ont évolué. Il manque à Java de nombreuses améliorations que les développeurs travaillant avec des langages plus modernes apprécient.

Kotlin bénéficie de l’expérience acquise car certaines décisions de conception prises lors de la création de Java (et d’autres langages, comme Scala) ont mal vieilli. Kotlin a énormément évolué par rapport aux langages plus anciens et a corrigé leurs principaux défauts. Vous en apprendrez davantage dans les prochains chapitres sur la façon dont Kotlin améliore Java et offre une expérience de développement plus fiable.

Kotlin n’est pas seulement un meilleur langage pour écrire du code pour fonctionner sur la machine virtuelle Java : il est aussi un langage multiplateforme généraliste ; Kotlin peut être utilisé pour écrire des applications MacOs et Windows natives, des applications JavaScript, et, bien sûr, des applications Android. Cette indépendance par rapport aux plateformes signifie que Kotlin a une grande variété d’utilisations.

1.2.  À qui est destiné ce livre ?

Nous avons écrit ce livre pour les développeurs de toutes sortes : développeurs Android expérimentés qui veulent des fonctionnalités modernes qui vont au-delà de ce que Java peut offrir, développeurs côté serveur intéressés par les fonctionnalités de Kotlin, et nouveaux développeurs qui cherchent à s’aventurer dans un langage compilé hautement performant.

La prise en charge d’Android est peut-être la raison pour laquelle vous avez ce livre entre vos mains, mais cet ouvrage ne se limite pas à la programmation Kotlin pour Android. En fait, sauf dans les deux derniers chapitres, tout le code Kotlin de ce livre est agnostique et n’est pas lié à l’infrastructure Android. Cela dit, si vous êtes intéressé par l’emploi de Kotlin pour le développement d’applications Android, ce livre montre quelques modèles courants qui font de l’écriture d’applications Android un jeu d’enfant en Kotlin.

Bien que Kotlin ait été influencé par un certain nombre d’autres langages, vous n’avez pas besoin de connaître les tenants et les aboutissants d’un autre langage pour apprendre Kotlin. De temps en temps, nous faisons le parallèle entre le code Java et le code Kotlin que vous avez écrit. Si vous avez déjà programmé en Java, cela vous aidera à comprendre la relation entre les deux langages. Si vous ne connaissez pas Java, voir comment un autre langage s’attaque aux mêmes problèmes vous aidera à saisir les principes qui ont façonné le développement de Kotlin.

1.3.  Comment utiliser ce livre

Ce livre n’est pas un guide de référence. Notre objectif est de vous guider à travers les parties les plus importantes du langage de programmation Kotlin. Vous allez travailler à travers des exemples, et approfondir vos connaissances au fur et à mesure de votre progression. Pour tirer le meilleur parti de ce livre, nous vous recommandons de saisir le code des exemples. En réalisant les projets que nous proposons, vous renforcerez la mémorisation du langage et vous pourrez exploiter vos travaux d’un chapitre à l’autre.

En effet, chaque chapitre s’appuie sur les sujets évoqués dans le chapitre précédent, si bien que nous vous recommandons de lire les chapitres dans l’ordre. Même si vous sentez que vous êtes familier avec un sujet dans d’autres langages, nous vous suggérons néanmoins de lire l’intégralité de ce livre, car Kotlin gère de nombreux problèmes de manière unique. Vous commencerez par des sujets élémentaires, comme les variables et les listes, puis vous attaquerez les techniques de programmation orientée objet et fonctionnelle, ce qui vous permettra de comprendre pourquoi Kotlin est un langage si puissant. À la fin du livre, vous serez passé du statut de programmeur Kotlin débutant à celui de développeur plus avancé.

Cela dit, prenez votre temps et élargissez votre horizon : consultez la référence du langage Kotlin qui est disponible à https://kotlinlang.org/docs/reference/ afin de compléter vos connaissances sur les sujets qui ont piqué votre curiosité, et faites des expériences.

Pour les plus curieux

La plupart des chapitres de ce livre ont une ou deux sections intitulées « Pour les plus curieux ». Beaucoup de ces sections éclairent les mécanismes sous-jacents du langage Kotlin. Les exemples décrits dans les chapitres ne dépendent pas des informations contenues dans ces sections dont nous vous conseillons néanmoins la lecture.

Défis

La plupart des chapitres se terminent par un ou plusieurs défis. Ce sont des problèmes supplémentaires à résoudre qui sont conçus pour améliorer votre compréhension de Kotlin. Nous vous encourageons à les relever afin d’améliorer votre maîtrise de Kotlin.

Conventions typographiques

Au fur et à mesure que vous créez les projets de ce livre, nous vous guiderons en vous présentant un sujet, puis en montrant comment appliquer vos nouvelles connaissances. Pour plus de clarté, nous nous en tenons aux conventions typographiques suivantes.

Les variables, les valeurs et les types s’affichent dans une police à espacement fixe. Les noms des classes, des fonctions et des interfaces sont imprimés en gras.

Tous les listings de code sont affichés dans une police à espacement fixe. Si vous devez saisir un code figurant dans un listing, ce code est imprimé en gras. Si vous devez supprimer du code dans un listing, ce code est barré. Dans l’exemple suivant, on vous demande de supprimer la ligne définissant la variable y et d’ajouter une variable appelée z :

var x = "Python"

var y = "Java"

var z = "Kotlin"

Prenez votre temps avec les exemples de ce livre. Une fois que vous serez familier avec la syntaxe de Kotlin, nous pensons que vous trouverez le processus de développement en Kotlin clair, pragmatique et fluide. D’ici là, gardez à l’esprit que l’apprentissage d’un nouveau langage peut être très gratifiant.

Kotlin est un langage relativement jeune, si bien que les conventions de codage ne sont pas encore fixées. Au fil du temps, vous développerez probablement votre propre style, mais nous avons tendance à adhérer aux guides de style Kotlin de JetBrains et de Google :

Conventions de codage de JetBrains :
https://kotlinlang.org/docs/reference/coding-conventions.html

Guide de style de Google, y compris les conventions pour le code Android et l’interopérabilité :
https://developer.android.com/kotlin/style-guide

2.  Remerciements

Nous avons bénéficié d’un grand soutien pour écrire ce livre. Sans cette aide, ce livre ne serait pas ce qu’il est, et il n’aurait peut-être même jamais vu le jour, si bien que nous devons exprimer notre gratitude envers de nombreuses personnes.

Tout d’abord, nous devons dire merci à nos collègues de Big Nerd Ranch. Merci à Stacy Henry et Aaron Hillegass de nous avoir donné le temps et l’espace pour écrire ce livre. Il a été extrêmement gratifiant d’apprendre et d’enseigner Kotlin. Nous espérons que ce livre sera à la hauteur de la confiance et du soutien que nous avons reçus.

Des remerciements particuliers sont également dus à nos autres collègues de Big Nerd Ranch. Votre enseignement attentif a révélé de nombreux bugs dans le texte, et vos recommandations éclairées ont conduit à de nombreuses améliorations dans notre approche. C’est vraiment merveilleux d’avoir des collègues comme vous. Merci Kristin Marsicano, Bolot Kerimbaev, Brian Gardner, Chris Stewart, Paul Turner, Chris Hare, Mark Allison, Andrew Lunsford, Rafael Moreno Cesar, Eric Maxwell, Andrew Bailey, Jeremy Sherman, Christian Keur, Mikey Ward, Steve Sparks, Mark Dalrymple, CBQ, et tous les autres du Ranch qui nous ont aidés dans l’élaboration de ce travail.

Nos collègues du service marketing et des ventes ont également été déterminants. Les cours ne seraient littéralement jamais programmés sans leur travail. Merci Heather Sharpe, Mat Jackson, Rodrigo « Ram Rod » Perez-Velasco, Nicholas Stolte, Justin Williams, Dan Barker, Israel Machovec, Emily Herman, Patrick Freeman, Ian Eze et Nikki Porter. Nous ne pouvons pas réaliser ce que nous faisons sans ce que vous faites.

Des remerciements spéciaux et un peu de karma supplémentaire sont également dus à nos étudiants fantastiques qui ont été assez aventureux pour se joindre à nous pour la première version de ce cours et qui ont eu la gentillesse de nous aider à identifier nos erreurs. Sans vos commentaires et vos idées sur la façon d’améliorer le cours, ce texte ne serait pas aussi abouti. Ces valeureux étudiants sont : Santosh Katta, Abdul Hannan, Chandra Mohan, Benjamin Di Gregorio, Peng Wan, Kapil Bhalla, Girish Hanchinal, Hashan Godakanda, Mithun Mahadevan, Brittany Berlanga, Natalie Ryan, Balarka Velidi, Pranay Airan, Jacob Rogers, Jean-Luc Delpech, Dennis Lin, Kristina Thai, Reid Baker, Setareh Lotfi, Harish Ravichandran, Matthew Knapp, Nathan Klee, Brian Lee, Heidi Muth, Martin Davidsson, Misha Burshteyn, Kyle Summers, Cameron Hill, Vidhi Shah, Fabrice Di Meglio, Jared Burrows, Riley Brewer, Michael Krause, Tyler Holland, Gajendra Singh, Pedro Sanchez, Joe Cyboski, Zach Waldowski, Noe Arzate, Allan Caine, Zack Simon, Josh Meyers, Rick Meyers, Stephanie Guevara, Abdulrahman Alshmrani, Robert Edwards, Maribel Montejano et Mohammad Yusuf.

Nous tenons à adresser des remerciements spéciaux à nos collègues et membres de la communauté Android qui nous ont aidés à tester l’exactitude du livre, sa clarté et sa lisibilité. Sans votre regard extérieur, nous aurions eu du mal à achever ce livre. Merci Jon Reeve, Bill Phillips, Matthew Compton, Vishnu Rajeevan, Scott Stanlick, Alex Lumans, Shauvik Choudhary et Jason Atwood.

Nous devons également remercier les nombreuses personnes talentueuses qui ont travaillé sur le livre avec nous. Elizabeth Holaday, notre éditrice, a contribué à améliorer le manuscrit et à diminuer ses faiblesses. Anna Bentley, notre relectrice, a trouvé et corrigé les erreurs et, au bout du compte, elle nous a fait paraître plus intelligents que nous ne le sommes.

Enfin, merci à tous nos étudiants. Être votre professeur nous offre aussi la possibilité d’être étudiant, à bien des égards, et nous vous en sommes infiniment reconnaissants. L’enseignement fait partie des plus grandes choses que nous faisons, et cela a été un plaisir de travailler avec vous. Nous espérons que la qualité de ce livre correspond à votre enthousiasme et à votre détermination.

1Votre première application Kotlin

Dans ce chapitre, vous allez écrire votre premier programme Kotlin à l’aide d’IntelliJ IDEA. Tout en accomplissant ce rituel qui est un passage obligé dans les ouvrages de programmation, vous vous familiariserez avec votre environnement de développement en créant un nouveau projet Kotlin, en écrivant et en exécutant du code Kotlin, puis en inspectant le résultat. Le projet que vous allez créer dans ce chapitre servira de bac à sable pour tester en douceur les nouveaux concepts que vous aborderez tout au long de ce livre.

1.  Installation d'IntelliJ IDEA

IntelliJ IDEA est un environnement de développement intégré (EDI) pour Kotlin créé par JetBrains (qui a également inventé le langage Kotlin). Pour commencer, téléchargez IntelliJ IDEA Community Edition à partir du site Web de JetBrains à l’adresse https://www.jetbrains.com/fr-fr/idea/download (figure 1.1).

Figure 1.1     Téléchargement de la version Community d’IntelliJ IDEA

Une fois que le fichier d’installation est téléchargé, suivez les instructions relatives à votre plateforme qui sont disponibles à :

https://www.jetbrains.com/help/idea/installation-guide.html

IntelliJ IDEA, que l’on appelle en abrégé IntelliJ, vous aide à écrire du code Kotlin bien formé. Cette application simplifie également le processus de développement avec des outils intégrés pour l'exécution, le débogage, l'inspection et le refactoring de votre code. Vous pouvez en apprendre plus sur les raisons pour lesquelles nous recommandons IntelliJ pour l’écriture du code Kotlin dans la section intitulée « Pour les plus curieux : pourquoi utiliser IntelliJ ? » qui se trouve à la fin de ce chapitre.

2.  Votre premier projet Kotlin

Félicitations, vous disposez à présent du langage de programmation Kotlin et d’un environnement de développement puissant. Maintenant, il ne vous reste plus qu'une chose à faire : apprendre à parler Kotlin couramment. Votre première mission consiste à créer un projet Kotlin.

Ouvrez IntelliJ et la boîte de dialogue de bienvenue à IntelliJ IDEA s’affichera (figure 1.2)

Figure 1.2     Boîte de dialogue de bienvenue

Si ce n'est pas la première fois que vous ouvrez IntelliJ depuis que vous l’avez installé, vous pouvez être dirigé directement vers le dernier projet que vous avez ouvert. Pour revenir à la boîte de dialogue de bienvenue, fermez le projet à l'aide de la commande File → Close Project.

Cliquez sur Create New Project. IntelliJ affiche la boîte de dialogue New Project. Dans la boîte de dialogue du nouveau projet, sélectionnez Kotlin dans le volet gauche, puis JVM|IDEA dans le volet droit (figure 1.3)

Figure 1.3     Création d’un projet JVM|IDEA

Vous pouvez utiliser IntelliJ pour écrire du code dans d’autres langages que Kotlin, notamment Java, Python, Scala et Groovy. En sélectionnant JVM|IDEA, vous dites à IntelliJ que vous avez l'intention d'utiliser Kotlin. Plus précisément, JVM|IDEA dit à IntelliJ que vous avez l'intention d'écrire du code Kotlin qui cible, ou s’exécute sur la machine virtuelle Java. Un des avantages de Kotlin est qu'il dispose d'une série d'outils qui permettent d'écrire du code Kotlin pouvant fonctionner sur différents systèmes d'exploitation et plateformes.

À partir de maintenant, nous nous référerons à la machine virtuelle Java sous l’appellation abrégée de « JVM », puisque c’est sous ce nom qu’elle est habituellement désignée dans la communauté des développeurs Java. Vous pourrez en apprendre davantage sur le choix de la JVM dans la section intitulée « Pour les plus curieux : choix du support de la JVM » à la fin de ce chapitre.

Cliquez sur Next dans la boîte de dialogue New Project. IntelliJ affiche une boîte de dialogue où vous pouvez choisir les paramètres de votre nouveau projet (figure 1.4). Dans le champ Projet name, saisissez « Sandbox » (bac à sable). Le champ d'emplacement du projet (Project location) sera complété automatiquement. Vous pouvez laisser tel quel l'emplacement sélectionné par défaut ou bien choisir un nouvel emplacement en cliquant sur le bouton ... situé à droite du champ. Sélectionnez la version Java 1.8 dans la liste déroulante Project SDK pour lier votre projet à la version 8 du Java Development Kit (JDK).

Figure 1.4     Attribution d’un nom au projet

Pour quelle raison avez-vous besoin du JDK afin d’écrire un programme Kotlin ? Le JDK donne à IntelliJ accès à la JVM ainsi qu’aux outils Java nécessaires à la conversion de votre code Kotlin en bytecode (vous en apprendrez plus sur ce sujet dans un instant). Techniquement, n'importe quelle version égale ou supérieure à la version 6 fonctionnera, mais selon notre expérience, la version 8 du JDK fonctionne mieux.

Si vous ne voyez pas la version 1.8 de Java répertoriée dans la liste déroulante Project SDK, cela signifie que vous n'avez pas encore installé la version 8 du JDK. Faites-le à présent : téléchargez la version correspondant à votre plateforme à partir de https://www.oracle.com/java/technologies/javase-jdk8-downloads.html. Installez le JDK, puis redémarrez IntelliJ. Suivez à nouveau les étapes décrites plus haut pour créer un nouveau projet. Si vous ne voyez toujours pas la bonne version du SDK, cliquez sur le bouton New… et sélectionnez la version du JDK dans l’arborescence Java.

Lorsque la boîte de dialogue des paramètres du projet ressemble à celle qui est illustrée à la figure 1.4, cliquez sur Finish.

IntelliJ génère alors un projet nommé Sandbox et affiche le nouveau projet dans une vue par défaut à deux volets (figure 1.5). Sur le disque, IntelliJ crée un dossier et un ensemble de sous-dossiers et de fichiers du projet à l'emplacement spécifié dans le champ Project location.

Figure 1.5     Affichage par défaut à deux volets

Le volet de gauche affiche la fenêtre du projet. Le volet de droite est actuellement vide. C'est à cet endroit que vous afficherez et modifierez le contenu de vos fichiers Kotlin dans l'éditeur. Reportez votre attention sur la fenêtre du projet dans le volet gauche. Cliquez sur la flèche située à gauche du nom du projet, Sandbox. L’arborescence se développe et affiche les fichiers contenus dans le projet, comme cela est illustré à la figure 1.6.

Figure 1.6     Affichage du projet

Un projet comprend tout le code source de votre programme, ainsi que des informations sur les dépendances et les configurations. Un projet peut être décomposé en un ou plusieurs modules, qui sont similaires à des sous-projets. Par défaut, un nouveau projet dispose d'un module, qui constitue tout ce dont vous avez besoin pour votre premier projet s’il est simple.

Le fichier Sandbox.iml contient des informations de configuration spécifiques à un module unique. Le dossier .idea contient des fichiers de paramètres pour l'ensemble du projet ainsi que ceux qui sont spécifiques à votre interaction avec le projet dans l'IDE (par exemple, les fichiers ouverts dans l'éditeur). Laissez tels quels ces fichiers générés automatiquement.

L'entrée External Libraries contient des informations sur les bibliothèques dont le projet dépend. Si vous développez cette entrée, vous verrez que IntelliJ a automatiquement ajouté Java 1.8 et KotlinJavaRuntime sous la forme de dépendances pour votre projet.

Vous pouvez en apprendre davantage sur la structure des projets IntelliJ sur le site Web de JetBrains à :

https://www.jetbrains.org/intellij/sdk/docs/basics/project_structure.html

Le dossier src est l'endroit où vous placerez tous les fichiers Kotlin que vous créez pour votre projet Sandbox. Il est temps à présent de créer et de modifier votre premier fichier Kotlin.

2.1.  Création de votre premier fichier Kotlin

Faites un clic droit sur le dossier src dans la fenêtre du projet. Sélectionnez New | Kotlin File/Class dans le menu qui apparaît (figure 1.7).

Figure 1.7     Création d’un nouveau fichier Kotlin

Dans la boîte de dialogue New Kotlin File/Class, saisissez « Hello » dans le champ, laissez la sélection par défaut sur le type File (figure 1.8) et validez votre saisie en appuyant sur la touche Entrée.

Figure 1.8     Attribution d’un nom au fichier

IntelliJ va créer un nouveau fichier dans votre projet, src/Hello.kt, et afficher le contenu du fichier dans l'éditeur sur le côté droit de la fenêtre IntelliJ (figure 1.9). L'extension .kt indique que le fichier est écrit en langage Kotlin, tout comme l'extension .java est utilisée pour les fichiers Java et .py pour les fichiers Python.

Figure 1.9     Affichage du fichier vide Hello.kt dans l'éditeur

Vous êtes enfin prêt à écrire du code Kotlin. Saisissez le code suivant dans l'éditeur (rappelez-vous que tout au long de ce livre, le code que vous devez saisir est imprimé en gras).

Listing 1. 1 « Hello world » en Kotlin (Hello.kt)

fun main() {

    println("Hello, world!")

}

Vous n’êtes peut-être pas très familier avec le code que vous venez d'écrire, mais ne craignez rien ! À la fin de ce livre, la lecture et l'écriture de code Kotlin sera comme une seconde nature. Pour l'instant, il suffit de comprendre les grandes lignes du code.

Le code du premier listing définit une nouvelle fonction. Une fonction est un groupe d'instructions qui peuvent être exécutés ultérieurement. Vous apprendrez en détail la manière de définir et de travailler avec les fonctions au chapitre 4.

Cette fonction particulière - la fonction main - a une signification spéciale dans Kotlin : elle indique le point de départ de votre programme. C'est ce qu'on appelle le point d'entrée de l'application, et un tel point d'entrée doit être défini pour votre application Sandbox (ou tout autre programme) afin qu’elle puisse s’exécuter. Chaque projet que vous écrirez dans ce livre commencera par une fonction main.

Votre fonction main contient une instruction : println("Hello world!"). println() est également une fonction et elle est intégrée dans la bibliothèque standard de Kotlin. Lors du lancement du programme, la fonction println("Hello world!") est exécutée, et IntelliJ affiche le contenu à l’intérieur des parenthèses (sans les guillemets, en l’occurrence Hello world! ) à l'écran.

2.2.  Exécution de votre fichier Kotlin

Peu de temps après avoir terminé de saisir le code du premier listing, IntelliJ affichera un triangle vert, que l’on appelle « bouton d'exécution », à gauche de la première ligne (figure 1.10). Si le l'icône n'apparaît pas, ou si vous voyez une ligne rouge sous le nom du fichier dans l'onglet ou sous l'une des lignes du code que vous avez saisi, cela signifie qu’il y a une erreur dans votre code. Vérifiez que vous avez tapé le code exactement comme il est indiqué dans le listing 1.1. D'autre part, si vous voyez le K de Kotlin symbolisé par un assemblage de formes rouges et bleues, cet indicateur a la même signification que le bouton d'exécution.

Figure 1.10     Bouton d’exécution

Il est temps d’exécuter votre programme afin qu’il salue le monde. Cliquez sur le bouton d’exécution. Sélectionnez Run 'HelloKt' dans le menu qui apparaît (figure 1.11). Cela indique à IntelliJ que vous voulez voir votre programme en action.

Figure 1.11     Exécution du programme Hello.kt

Lorsque vous exécutez votre programme, IntelliJ exécute le code à l'intérieur des accolades ({}), ligne par ligne, puis met fin à l'exécution du code. Deux nouvelles fenêtres sont également affichées au bas de la fenêtre IntelliJ (figure 1.12).

Figure 1.12     Fenêtres de la console et du journal des événements

Sur la gauche se trouve la fenêtre Exécution, que l’on appelle également console (dénomination que nous emploierons à partir de maintenant). Elle affiche des informations sur ce qui s'est passé quand IntelliJ a exécuté votre programme, ainsi que toute sortie affichée par votre programme. Vous devriez voir le message « Hello, world! » affiché dans votre console. Vous devriez également voir que le processus s’est terminé avec le code de sortie 0, ce qui indique que le programme s’est terminé sans erreur. Cette ligne apparaît à la fin de la sortie de la console lorsque tout s’est bien passé et nous ne la montrerons plus dans les résultats de la console à partir de maintenant.

Les utilisateurs de Mac peuvent voir un message d’erreur (objc[15752]: Class JavaLaunchHelper is implemented in both /LibraryJava/JavaVirtualMachine…) s’afficher en rouge indiquant qu'il y a un problème avec JavaLauncherHelper, mais vous pouvez ne pas tenir compte de cet avertissement. Il s’agit d’une conséquence malheureuse de la façon dont l'environnement Java Runtime est installé sur macOS. Supprimer ce message d’erreur exigerait beaucoup d'efforts, et comme le problème est sans gravité, vous pouvez l'ignorer.

Sur la droite se trouve la fenêtre du journal des événements qui affiche des informations sur le travail réalisé par IntelliJ pour que votre programme soit prêt à s’exécuter. Nous ne mentionnerons plus à l’avenir le journal de l'événement parce que les informations les plus intéressantes se situent dans la console (pour la même raison, ne vous inquiétez pas si le journal des événements ne s’est pas ouvert quand vous avez exécuté votre programme). Vous pouvez fermer la fenêtre avec le bouton qui sert à la masquer et qui se trouve en haut à droite (icône en forme de flèche vers le bas).

Compilation et exécution du code Kotlin/JVM

Il se passe beaucoup de choses dans le court laps de temps où vous cliquez sur le bouton Run 'HelloKt' et le moment où vous voyez Hello, world! s’afficher dans la console.

Tout d'abord, IntelliJ compile le code Kotlin à l'aide du compilateur kotlinc-jvm. Cela signifie qu'IntelliJ traduit le code Kotlin que vous avez écrit en bytecode, le langage que « parle » la JVM. Si kotlinc-jvm a des problèmes à traduire votre code Kotlin, il affichera un ou plusieurs messages d'erreur, qui vous donneront des indices sur la façon de résoudre les problèmes. Si le processus de compilation se déroule sans heurts, IntelliJ passe à la phase d'exécution.

Dans la phase d'exécution, le bytecode qui a été généré par kotlinc-jvm est exécuté sur la JVM. La console affiche toute sortie de votre programme, par exemple elle affiche le texte que vous avez spécifié dans votre appel à la fonction println(), au fur et à mesure que la JVM exécute les instructions.

Quand il n'y a plus d'instructions de bytecode à exécuter, la JVM s’arrête. IntelliJ affiche dans la console un message indiquant si l'exécution s'est terminée avec succès et, dans le cas contraire, IntelliJ signale un code d'erreur.

Vous n'aurez pas besoin d'une compréhension complète du processus de compilation mis en œuvre par Kotlin pour profiter de cet ouvrage, mais nous étudierons cependant le bytecode plus en détail au chapitre 2.

3.  Kotlin REPL

Il arrive parfois que l’on souhaite tester un bout de code Kotlin pour voir ce qui se produit quand on l'exécute, un peu de la même manière que l’on utilise du papier brouillon pour noter les étapes d’un calcul. Ceci est particulièrement utile quand vous apprenez le langage Kotlin. La bonne nouvelle est qu’IntelliJ fournit un tel outil pour tester rapidement du code sans avoir à créer un fichier. Cet outil se nomme Kotlin REPL ; nous allons expliquer son nom dans un instant, mais pour l'instant, nous allons l'ouvrir et voir ce qu'il peut faire.

Dans IntelliJ, ouvrez la fenêtre Kotlin REPL en sélectionnant Tools,​ Kotlin puis Kotlin​ REPL (figure 1.13).

Figure 1.13     Ouverture de la fenêtre Kotlin REPL

IntelliJ affiche l’outil REPL au bas de la fenêtre (figure 1.14).

Figure 1.14     Fenêtre Kotlin REPL

Vous pouvez saisir du code dans cette fenêtre, de la même manière que dans l'éditeur. La différence est que vous pouvez l’évaluer rapidement, sans compiler la totalité du projet.

Entrez le code suivant dans REPL :

Listing 1.2 "Bonjour, Kotlin !" (REPL)

println("Bonjour, Kotlin !")

Une fois que vous avez saisi le texte, appuyez sur la combinaison de touches Ctrl + Entrée pour évaluer le code. Après un moment, vous verrez la sortie s’afficher en dessous (Bonjour, Kotlin !) qui est illustrée à la figure 1.15.

Figure 1.15     Évaluation du code

REPL est l’abréviation de Read Evaluate Print Loop (Lire, Évaluer, Afficher, Boucle). Vous pouvez saisir un bout de code dans l'invite de commande et le soumettre en cliquant sur le bouton d'exécution représenté par un triangle vert sur le côté gauche de REPL ou bien en appuyant sur les touches Ctrl + Entrée. REPL lit ensuite le code, l’évalue (c’est-à-dire l’exécute), et affiche la valeur ou l'effet qui est le résultat de l’exécution. Une fois que REPL a terminé l'exécution, il vous rend le contrôle et vous pouvez à nouveau (d’où l’idée de boucle) relancer le processus de test du code saisi autant de fois que vous le souhaitez.

Vous venez de débuter votre apprentissage de la programmation Kotlin et vous avez déjà accompli beaucoup de choses dans ce chapitre. Dans le chapitre suivant, vous commencerez à approfondir les détails du langage en étudiant l’emploi des variables, des constantes et des types afin de représenter des données.

4.  Pour les plus curieux : pourquoi utiliser IntelliJ ?

On peut écrire un programme Kotlin à l'aide de n'importe quel éditeur de texte ordinaire. Cependant, nous vous recommandons d'utiliser IntelliJ, d'autant plus si vous êtes en phase d’apprentissage de ce langage. De la même manière qu’un traitement de texte offrant les services d’un vérificateur orthographique et grammatical facilite l'écriture d'une dissertation, IntelliJ simplifie l'écriture de code Kotlin bien formé. IntelliJ vous aide notamment à :

écrire du code syntaxiquement et sémantiquement correct avec des fonctionnalités comme la coloration syntaxique, les suggestions contextuelles, et la complétion automatique du code ;

exécuter et déboguer votre code avec des fonctionnalités comme les points d'arrêt et le mode pas à pas du code lorsque votre application est en cours d'exécution ;

restructurer le code existant avec des raccourcis de refactoring (comme l’extraction des constantes et leur renommage) et la mise en forme du code afin d’améliorer l'indentation et l'espacement.

En outre, comme Kotlin a été créé par JetBrains, l'intégration entre IntelliJ et Kotlin est parfaite, ce qui facilite grandement la modification du code. Avantage supplémentaire, IntelliJ constitue la base d'Android Studio, de telle sorte que les raccourcis et les outils que vous apprenez dans ce livre sont un bon investissement si vous utilisez Android Studio par la suite.

5.  Pour les plus curieux : choix du support de la JVM

La JVM est un logiciel qui sait comment exécuter un ensemble d'instructions, appelé bytecode. Quand on fait le choix du support de la JVM (dans le jargon des informaticiens, on dit aussi « cibler » la JVM), cela signifie que l’on va compiler ou traduire le code source Kotlin en bytecode Java, avec l'intention d'exécuter ce bytecode sur la JVM (figure 1.16).

Figure 1.16     Compilation et flux d’édition

Chaque plate-forme, comme Windows ou macOS, a son propre ensemble d'instructions. La JVM agit comme une passerelle entre le bytecode et les différents environnements matériels et logiciels sur lesquels la JVM s'exécute, en lisant un morceau de bytecode et en appelant les instructions spécifiques qui correspondent à la plateforme qui prend en charge ce bytecode. Par conséquent, il existe différentes versions de la JVM pour différentes plateformes. C'est ce qui permet aux développeurs Kotlin d'écrire une seule fois un code indépendant des plateformes, qui est ensuite compilé en bytecode, puis exécuté sur différents appareils indépendamment de leur système d'exploitation.

Comme Kotlin peut être converti en bytecode que la JVM peut exécuter, on le considère comme un langage JVM. Java est sans doute le langage JVM le plus connu, parce qu’il a été le premier, mais d'autres langages JVM, tels que Scala et Kotlin, sont apparus en réponse à certaines lacunes de Java du point de vue du développeur.

Kotlin n'est cependant pas limité à la JVM. À l’heure actuelle, Kotlin peut également être compilé en JavaScript ou même dans des formats binaires natifs qui s'exécutent directement sur une plateforme donnée (Windows, Linux, et macOS), ce qui supprime le besoin d'une couche de machine virtuelle.

6.  Défi : test des opérateurs arithmétiques dans REPL

Un certain nombre de chapitres de ce livre se terminent par un ou plusieurs défis. Ces défis sont pour vous l’occasion d’approfondir votre compréhension de Kotlin et d’enrichir votre expérience.

Utilisez REPL pour explorer le fonctionnement des opérateurs arithmétiques dans Kotlin : +, -, *, /, et %. Par exemple, saisissez (9 + 12) * 2 dans REPL. La sortie correspond-elle à ce à quoi vous vous attendiez ?

Si vous souhaitez étudier en détail les fonctions mathématiques disponibles dans la bibliothèque standard de Kotlin, consultez la page Web disponible à https://kotlinlang.org/api/latest/jvm/stdlib/kotlin.math/ et testez-les dans REPL. Par exemple, essayez min (94, -99), qui vous renverra le minimum des deux nombres entre parenthèses.

2Variables, constantes, et types

Dans ce chapitre, nous allons vous présenter les notions de variables et de constantes, ainsi que les types de données de base de Kotlin, qui sont les éléments fondamentaux de tout programme. Vous utiliserez des variables et des constantes pour stocker les valeurs et transmettre des données à votre application. Les types décrivent les caractéristiques des données qui sont stockées dans une constante ou une variable.

Il existe d’importantes différences entre chacun des types de données et entre les variables et les constantes qui conditionnent la façon dont ils sont utilisés.

1.  Types

Les variables et les constantes ont un type de données qu’il faut spécifier. Le type décrit les données qui sont stockées dans une variable ou une constante et il indique au compilateur comment la vérification du type sera gérée ; il s’agit d’une fonctionnalité de Kotlin qui empêche l’affectation du mauvais type de données à une variable ou une constante.

Pour illustrer cette notion, vous allez ajouter un fichier au projet Sandbox que vous avez créé au chapitre 1. Ouvrez IntelliJ. Le projet Sandbox s’ouvrira probablement automatiquement, car IntelliJ rouvre votre projet le plus récent. Si ce n’est pas le cas, vous pouvez ouvrir le projet Sandbox à partir de la liste des fichiers récents qui se trouve sur le côté gauche de la boîte de dialogue de bienvenue ou en sélectionnant File → Open Recent → Sandbox.

Commencez par ajouter un nouveau fichier au projet en faisant un clic droit sur le dossier src dans la fenêtre du projet (il faudra peut-être cliquer sur la flèche du projet Sandbox pour visualiser le dossier src.). Sélectionnez New → Kotlin / File Class, et attribuez le nom TypeIntro au fichier. Le nouveau fichier s’ouvrira dans l’éditeur.

La fonction main, comme vous l’avez vu dans le chapitre 1, définit le point d’entrée de votre programme. IntelliJ offre un raccourci pour écrire cette fonction : saisissez le mot « main » dans TypeIntro.kt et appuyez sur la touche Tabulation. IntelliJ ajoutera automatiquement les éléments de base de la fonction, comme cela est indiqué dans le listing 2.1.

Listing 2.1 Ajout d’une fonction main (TypeIntro.kt)

fun main() {

}

2.  Déclaration d’une variable

Imaginez que vous écriviez un jeu d’aventure qui permet à un joueur d’explorer un monde interactif. Vous voudrez peut-être qu’une variable puisse garder la trace du score du joueur.

Dans TypeIntro.kt, créez votre première variable, appelée experiencePoints, et assignez-lui une valeur :

Listing 2.2 Déclaration d’une variable experiencePoints (TypeIntro.kt)

fun main() {

    var experiencePoints: Int = 5

    println(experiencePoints)

}

Dans cet exemple, vous avez assigné une instance du type Int à une variable appelée experiencePoints. Examinons chacune des étapes de ce qui vient de se passer.

Vous avez défini une variable en utilisant le mot-clé var, qui indique que vous voulez déclarer une nouvelle variable, suivi de son nom.

Ensuite, vous avez spécifié la définition du type de la variable, : Int, qui indique que experiencePoints va stocker un entier.

Enfin, vous avez utilisé l’opérateur d’assignation (=) pour assigner la partie à droite du signe (une instance du type Int, en l’occurrence 5) à la partie à gauche du signe (experiencePoints).

La figure 2.1 illustre la définition de la variable experiencePoints sous la forme d’un diagramme.

Figure 2.1     Anatomie d’une définition de variable

Après avoir défini la variable, on affiche sa valeur dans la console en employant la fonction println.

Lancez le programme en cliquant sur le bouton d’exécution, qui est situé à côté de la fonction main et sélectionnez Run 'TypeIntroKt'. Le résultat affiché dans la console est égal à 5, la valeur que vous avez assignée à experiencePoints.

À présent, essayez d’assigner à experiencePoints la valeur "trente" (le texte barré indique le code que vous devez supprimer).

Listing 2.3 Assignation de la valeur "trente" à experiencePoints (TypeIntro.kt)

fun main() {

    var experiencePoints: Int = 5

    var experiencePoints: Int = "trente"

    println(experiencePoints)

}

Lancez à nouveau la fonction main en cliquant sur le bouton d’exécution. Cette fois-ci, le compilateur de Kotlin affiche un message d’erreur :

Error:(2, 33) Kotlin: Type mismatch: inferred type is String but Int was expected

Quand vous avez saisi ce code, vous avez peut-être remarqué que "trente" était souligné en rouge. IntelliJ vous indique que le programme comporte une erreur. Si vous survolez avec votre souris le libellé "trente", vous pourrez lire les détails sur le problème qui a été détecté (figure 2.2).

Figure 2.2     Signalement d’une erreur de type

Kotlin utilise un système de types statiques, c’est-à-dire que le compilateur étiquette le code source que vous définissez avec des types afin de s’assurer que le code que vous avez écrit est valide. IntelliJ vérifie également le code au fur et à mesure que vous le saisissez et vous signale quand une instance d’un type particulier est attribuée de manière incorrecte à une variable d’un type différent. Cette fonctionnalité, qui s’appelle vérification de type statique, vous renseigne sur les erreurs de programmation avant même la compilation du programme.

Pour supprimer l’erreur, modifiez la valeur assignée à experiencePoints en rétablissant un type Int qui correspond au type déclaré : changez la valeur "trente" en 5 :

Listing 2.4 Résolution de l’erreur de type (TypeIntro.kt)

fun main() {

    var experiencePoints: Int = "trente"

    var experiencePoints: Int = 5

    println(experiencePoints)

}

On peut modifier la valeur d’une variable au cours d’un programme. Par exemple, si le joueur acquiert plus d’expérience, on peut assigner une nouvelle valeur à la variable experiencePoints. Ajoutez 5 à la variable experiencePoints, de la manière suivante :

Listing 2.5 Ajout de la valeur 5 à la variable experiencePoints (TypeIntro.kt)

fun main() {

    var experiencePoints: Int = 5

    experiencePoints += 5

    println(experiencePoints)

}

Après avoir attribué à la variable experiencePoints la valeur 5, on utilise l’opérateur d’addition et d’affectation (+=) pour ajouter 5 à la valeur d’origine. Exécutez le programme à nouveau. Vous verrez que le nombre 10 s’affiche dans la console.

3.  Types prédéfinis de Kotlin

Vous avez déjà rencontré des variables de type String et des variables du type Int. Kotlin possède également des types pour gérer des valeurs comme vrai/faux, des listes d’éléments et des paires clé-valeur pour définir des mappings d’éléments. Le tableau 2.1 liste la plupart des types intégrés couramment utilisés en Kotlin :

Tableau 1.  Types prédéfinis couramment utilisés en Kotlin

Type

Description

Exemples

String

Données textuelles

"Estragon"

"Bon appétit"

Char

Caractère unique

'X'

Caractère Unicode U+0041

Boolean

Valeur true/false

true

false

Int

Nombres entiers

"Estragon".length

5

Double

Nombres décimaux

3.14

2.718

List

Collections d’éléments

3, 1, 2, 4, 3

"bière", " soda", "coca"

Set

Collections d’éléments uniques

"Athos", "Porthos", "Aramis"

"Mercure", "Venus", "Terre", "Mars", "Jupiter", "Saturne", "Uranus", "Neptune"

Map

Collections de paires clé-valeur

"small" / 5.99, "medium" / 7.99, "large" / 10.99

Si vous n’avez pas encore rencontré tous ces types, ne vous inquiétez pas car vous ferez leur connaissance tout au long de ce livre. En particulier, vous en apprendrez davantage sur les chaînes de caractères au chapitre 7 et sur les nombres au chapitre 8 ; vous étudierez les listes, les ensembles et les maps, que l’on regroupe sous la dénomination de types collection aux chapitres 10 et 11.

4.  Variables en lecture seule

Jusqu’à présent, vous avez vu des variables dont les valeurs peuvent être réaffectées, mais il arrive souvent que l’on souhaite utiliser des variables dont les valeurs ne sont pas censées changer dans un programme. Par exemple, dans le jeu d’aventure, le nom du joueur ne changera pas une fois qu’il a été initialisé.

Kotlin fournit une syntaxe différente pour déclarer les variables en lecture seule, c’est-à-dire les variables qui ne peuvent pas être modifiées une fois qu’on leur a assigné une valeur.

On déclare une variable qui peut être modifiée à l’aide du mot-clé var, alors que l’on déclare une variable en lecture seule avec le mot-clé val.

Dans le jargon Kotlin, les variables dont les valeurs peuvent changer sont appelées vars et les variables en lecture seule sont appelées vals. Nous suivrons cette convention à partir de maintenant, mais nous continuerons à les désigner collectivement sous le nom de « variables ».

Ajoutez une définition d’une val pour stocker le nom du joueur et l’afficher après son nombre de points d’expérience :

Listing 2.6 Ajout d’une val playerName (TypeIntro.kt)

fun main() {

    val playerName: String = "Estragon"

    var experiencePoints: Int = 5

    experiencePoints += 5

    println(experiencePoints)

    println(playerName)

}

Exécutez le programme en cliquant sur le bouton d’exécution à côté de la fonction main et en sélectionnant Run 'TypeIntroKt'. Vous verrez les valeurs des variables experiencePoints et playerName s’afficher dans la console :

10

Estragon

Ensuite, tentez de réassigner une valeur String différente à playerName en utilisant l’opérateur d’affectation =, et exécutez à nouveau le programme.

Listing 2.7 Tentative de modification de la valeur de playerName (TypeIntro.kt)

fun main() {

    val playerName: String = "Estragon"

    playerName = "Madrigal"

    var experiencePoints: Int = 5

    experiencePoints += 5

    println(experiencePoints)

    println(playerName)

}

Vous verrez l’erreur de compilation suivante :

    Error:(3, 5) Kotlin: Val cannot be reassigned

Le compilateur proteste car vous avez tenté de modifier une val. Quand on a affecté une valeur à une val, elle ne peut plus être réassignée.

Supprimez la deuxième affectation pour corriger l’erreur :

Listing 2.8 Correction de l’erreur provoquée en réassignant une val (TypeIntro.kt)

fun main() {

    val playerName: String = "Estragon"

    playerName = "Madrigal"

    var experiencePoints: Int = 5

    experiencePoints += 5

    println(experiencePoints)

    println(playerName)

}

Les vals sont utiles pour se prémunir contre les modifications accidentelles de variables qui doivent rester en lecture seule. C’est pour cette raison que nous vous recommandons d’utiliser une val chaque fois que vous n’avez pas besoin d’une var.

IntelliJ peut détecter quand une var peut remplacer une val en analysant votre code. Si une var n’est jamais modifiée, IntelliJ vous suggérera de la convertir en une val. Nous vous suggérons de suivre la suggestion d’IntelliJ, à moins que vous ne soyez sur le point d’écrire le code qui réaffecte la var. Pour voir à quoi ressemble la suggestion d’IntelliJ, transformez playerName en var :

Listing 2.9 Modification de playerName pour qu’il devienne réassignable (TypeIntro.kt)

fun main() {

    val playerName: String = "Estragon"

    var playerName: String = "Estragon"

    var experiencePoints: Int = 5

    experiencePoints += 5

    println(experiencePoints)

    println(playerName)

}

Comme la valeur de playerName n’est jamais réaffectée, la variable n’a pas besoin d’être une var. Notez qu’IntelliJ a surligné en jaune la ligne contenant le mot-clé var. Si vous passez votre souris sur le mot-clé var, IntelliJ explique l’amélioration qu’il suggère (figure 2.3).

Figure 2.3     Variable qui n’est jamais modifiée

Comme prévu, IntelliJ suggère que dans la mesure où la variable playerName n’est jamais modifiée, on peut la transformer en une val. Pour appliquer la suggestion, cliquez sur le mot-clé var à côté de playerName et appuyez sur les touches Alt + Entrée (Option + Entrée sur Mac). Dans la fenêtre contextuelle, sélectionnez Change to val (figure 2.4).

Figure 2.4     Transformation en variable immutable

IntelliJ convertit automatiquement la var en une val (figure 2.5).

Figure 2.5     La variable playerName est devenue immutable

Comme nous l’avons dit plus haut, nous vous recommandons d’utiliser une val chaque fois que vous le pouvez, afin que Kotlin puisse vous avertir des réaffectations accidentelles. Nous vous recommandons également de prêter attention aux suggestions qu’IntelliJ propose pour améliorer votre code. Vous ne pourrez pas toujours les utiliser, mais cela vaut toujours la peine d’y jeter un œil.

5.  Inférence de type

Vous noterez que les définitions de type que vous avez spécifiées pour les variables playerName et experiencePoints sont grisées dans IntelliJ. Le texte grisé indique un élément qui n’est pas nécessaire. Passez la souris sur la définition de type String, et IntelliJ vous fournira une explication sur les raisons pour lesquelles l’élément n’est pas nécessaire (figure 2.6).

Figure 2.6     Information de type redondante

Comme vous pouvez le constater, Kotlin indique que votre déclaration de type est « redondante ». Qu’est-ce que cela veut dire ?

Kotlin inclut une fonctionnalité appelée inférence de type qui permet d’omettre la définition de type pour les variables qui se voient attribuer une valeur lorsqu’elles sont déclarées. Comme vous attribuez des données de type String à playerName et de type Int à experiencePoints lorsque vous les déclarez, le compilateur Kotlin déduit les informations de type appropriées pour les deux variables.

Tout comme IntelliJ peut vous aider à changer une var en val, il peut également vous aider à supprimer les spécifications de type inutiles. Cliquez sur la définition de type String (: String) à côté de playerName et appuyez sur Alt + Entrée (Option + Entrée sur Mac). Choisissez ensuite dans la fenêtre la commande Remove explicit type specification (figure 2.7).

Figure 2.7     Suppression d’une spécification de type explicite

: String va alors disparaître. Répétez le processus pour la var experiencePoints et supprimez la spécification de type : Int.

Que vous profitiez de l’inférence de type ou que vous spécifiiez le type lorsque vous déclarez la variable, le compilateur gardera une trace du type. Dans ce livre, nous utilisons l’inférence de type quand cela est possible sans créer de l’ambiguïté. L’inférence de type aide à garder le code propre et concis, tout en facilitant sa modification au fur et à mesure que votre programme évolue.

Notez qu’IntelliJ affichera le type de toute variable sur demande, y compris celles qui utilisent l’inférence de type. Si jamais vous avez un doute sur le type d’une variable, cliquez sur son nom et appuyez sur Ctrl + Maj + P. IntelliJ affichera son type (figure 2.8).

Figure 2.8     Affichage d’information sur le type

6.  Constantes de compilation

Plus haut, nous vous avons dit que la valeur des vars peut être modifiée alors que ce n’est pas le cas des vals. En fait, il s’agissait d’un pieux mensonge… En réalité, il y a des cas particuliers où une val peut retourner des valeurs différentes, et nous en discuterons au chapitre 12. Si vous avez une donnée qui doit absolument, rester immuable (afin qu’elle ne puisse jamais être modifiée) envisagez plutôt une constante de compilation.

Une constante de compilation doit être définie en dehors de toute fonction, y compris la fonction main, parce que sa valeur doit être assignée au moment de la compilation (c’est-à-dire lorsque le programme se compile), d’où son nom. La fonction main et vos autres fonctions sont appelées pendant le temps d’exécution (runtime, c’est-à-dire lorsque le programme est exécuté), et c’est à ce moment-là que des valeurs sont attribuées aux variables. Une constante de compilation existe avant que l’affectation des variables n’ait lieu.

Les constantes de compilation doivent également être de l’un des types de base suivants, parce que l’utilisation de types plus complexes pour une constante pourrait compromettre la garantie de compilation. Vous en apprendrez davantage sur la façon dont les types sont construits au chapitre 13. Voici les types de base pris en charge pour une constante de compilation :

String

Int

Double

Float

Long

Short

Byte

Char

Boolean

Ajoutez une constante de compilation à TypeIntro.kt, au-dessus de la déclaration de la fonction main, en utilisant le modificateur const :

Listing 2.10 Déclaration d’une constante de compilation (TypeIntro.kt)

const val MAX_EXPERIENCE: Int = 5000

fun main() {

    ...

}

L’ajout à une val du modificateur const dit au compilateur qu’il peut être sûr que cette val ne changera jamais. Dans ce cas, on garantit que la constante MAX_EXPERIENCE, quoi qu’il arrive, aura toujours la valeur 5000. Cela donne au compilateur la flexibilité nécessaire pour effectuer l’optimisation en coulisses.

Vous vous interrogez peut-être sur le format du nom de la constante MAX_EXPERIENCE. Bien que ce format ne soit pas exigé par le compilateur, nous préférons distinguer les constantes en les écrivant en majuscules et en remplaçant les espaces par des barres de soulignement. Comme vous l’avez peut-être remarqué, nous utilisons le style camel case avec une minuscule à l’initiale à la fois pour les vars et les vals. Les normes de style comme celles-ci améliorent la lisibilité et la clarté de votre code.

7.  Inspection du bytecode Kotlin

Vous avez appris au chapitre 1 que Kotlin est une alternative à Java pour l’écriture de programmes qui fonctionnent dans la JVM, où le bytecode Java est exécuté. Il est souvent utile d’inspecter le bytecode Java que le compilateur Kotlin génère afin qu’il fonctionne dans la JVM. Nous examinerons le bytecode plusieurs fois dans ce livre car il s’agit d’une méthode pour analyser la manière dont une fonctionnalité d’un langage particulier est implémentée dans la JVM.

Savoir comment inspecter l’équivalent Java du code Kotlin que vous écrivez est une excellente technique pour comprendre comment fonctionne Kotlin, surtout si vous avez déjà programmé en Java. Si vous n’avez pas d’expérience en Java, le code Java partagera probablement certaines caractéristiques familières avec un langage dans lequel vous avez déjà programmé (vous pourrez alors le considérer comme un pseudocode qui facilitera votre compréhension). Et, si vous êtes un novice en programmation, nous vous adressons toutes nos félicitations ! En choisissant Kotlin, vous avez opté pour un langage qui, comme vous le verrez dans cet ouvrage, permet d’exprimer la même logique que Java, mais généralement en beaucoup moins de code.

Par exemple, vous vous êtes peut-être demandé comment l’utilisation de l’inférence de type lors de la définition des variables dans Kotlin affecte le bytecode qui en résulte et qui est généré pour s’exécuter sur la JVM. Pour répondre à cette question, vous pouvez utiliser la fenêtre bytecode Kotlin.

Dans TypeIntro.kt, appuyez deux fois sur la touche Maj pour ouvrir la boîte de dialogue Search Everywhere. Commencez par saisir « show kotlin bytecode » dans la zone de recherche, puis sélectionnez Show Kotlin Bytecode dans la liste des actions disponibles quand le résultat de la recherche apparaît (figure 2.9).

Figure 2.9     Recherche de la fonctionnalité d’affichage du bytecode Kotlin

La fenêtre d’affiche du bytecode Kotlin s’ouvre (figue 2.10). Vous pouvez également ouvrir cette fenêtre grâce à la commande Tools → Kotlin → Show Kotlin Bytecode.

Figure 2.10     Fenêtre d’affichage du bytecode Kotlin

Si le bytecode n’est pas votre langue maternelle, ne vous inquiétez pas ! Vous pouvez retraduire le bytecode en Java afin de lire un programme employant un langage avec lequel vous êtes plus familier. Dans la fenêtre de l’outil bytecode, cliquez sur le bouton Decompile en haut à gauche.

Un nouvel onglet s’ouvre et affiche TypeIntro.decompiled.java (figure 2.11) ; il s’agit d’une version Java du bytecode que le compilateur Kotlin a généré pour la JVM.

Figure 2.11     Affichage du bytecode décompilé

Les mots soulignés en rouge dans ce cas sont dus à une bizarrerie dans l’interaction entre Kotlin et Java, plutôt qu’à un problème.

Concentrez-vous sur les déclarations des variables experiencePoints et playerName :

String playerName = "Estragon";

int experiencePoints = 5;

Bien que vous ayez omis les déclarations de type dans les définitions des deux variables dans le code source Kotlin, le bytecode qui a été généré inclut des définitions explicites de type. C’est de cette manière que les variables seraient déclarées en Java, et le bytecode donne un aperçu de ce qui se passe en coulisses lors de la prise en charge de l’inférence de type dans Kotlin.

Vous approfondirez le bytecode Java décompilé dans les chapitres suivants. Pour l’instant, fermez TypeIntro.decompiled.java (en utilisant le bouton X de fermeture dans son onglet) et la fenêtre d’outil bytecode (en utilisant l’icône en haut à droite).

Dans ce chapitre, vous avez appris à stocker des données de base dans des vars et des vals ; vous avez également vu quelle sorte de variable il faut utiliser, selon que vous devez modifier ou non leurs valeurs. Vous avez vu comment déclarer des valeurs immutables en utilisant des constantes de compilation. Enfin, vous avez appris comment Kotlin tire parti de la puissance de l’inférence de type pour vous économiser la saisie de mots-clés chaque fois que vous déclarez une variable. Vous utiliserez l’ensemble de ces outils de base continuellement tout au long de ce livre.

Dans le chapitre suivant, vous apprendrez à représenter des états plus complexes en utilisant des conditions.

8.  Pour les plus curieux : types primitifs Java en Kotlin

En Java, il existe deux sortes de types : les types référence et les types primitifs. Les types référence sont définis dans le code source où une définition correspond à un type. Java offre également des types primitifs (souvent appelés simplement « primitifs »), qui n’ont pas de définition dans le code source, mais qui sont représentés par des mots-clés spéciaux.

Un type référence en Java commence toujours par une lettre majuscule, ce qui indique qu’il existe une définition de son type dans le code source. Voici une définition de experiencePoints créée à l’aide d’un type référence Java :

Integer experiencePoints = 5;

Un type primitif Java commence par une lettre minuscule :

int experiencePoints = 5;

Tous les primitifs Java ont un type référence correspondant (mais la réciproque n’est pas vraie). Pourquoi utiliser l’un plutôt que l’autre ?

On choisit en général un type référence car il existe certaines fonctionnalités du langage Java qui ne sont disponibles que lorsque l’on utilise des types référence. Les génériques, par exemple, que vous étudierez au chapitre 17, ne fonctionnent pas avec les primitifs. Les types référence peuvent également fonctionner avec les caractéristiques orientées objet de Java plus facilement que les primitifs Java (vous en apprendrez davantage sur la programmation orientée objet et les caractéristiques orientées objet de Kotlin au chapitre 12).

D’un autre côté, les primitifs offrent de meilleures performances et quelques autres avantages.

Contrairement à Java, Kotlin ne fournit qu’une seule sorte de type : les types référence.

var experiencePoints: Int = 5

Kotlin a fait ce choix de conception pour plusieurs raisons. Tout d’abord, quand il n’y a pas le choix entre différentes sortes de types, vous ne pouvez pas vous mettre dans une situation périlleuse aussi facilement que lorsque vous pouvez choisir entre plusieurs types. Par exemple, que se passe-t-il si vous définissez une instance de type primitif, et que vous vous rendez compte plus tard que vous devez utiliser un générique, qui nécessite un type référence ? Dans la mesure où Kotlin n’a que des types référence, cela signifie que vous ne rencontrerez jamais ce problème.

Si vous êtes familier avec Java, vous vous dites peut-être : « Mais les primitifs offrent de meilleures performances que les types référence ! ». C’est vrai, mais examinez la variable experiencePoints dans le bytecode décompilé que vous avez vu plus haut :

  int experiencePoints = 5;

Comme vous pouvez le constater, un type primitif a été utilisé à la place du type référence. Quelle en est la raison puisque Kotlin n’a que des types référence ? Le compilateur Kotlin utilise, dans la mesure du possible, des primitifs dans le bytecode Java, car ils offrent en effet de meilleures performances.

Kotlin vous offre la facilité des types référence avec la performance des primitifs grâce à l’optimisation du compilateur. Dans Kotlin, vous trouverez un type référence correspondant aux huit types primitifs que vous connaissez sans doute en Java.

9.  Défi : acquisition d’une monture

Voici votre premier défi. Dans le jeu d’aventure, les joueurs peuvent acquérir un dragon ou un minotaure qu’ils peuvent chevaucher. Définissez une variable appelée hasSteed (steed signifie monture en anglais) pour savoir si le joueur en a fait l’acquisition. Attribuez à la variable un état initial qui indique que le joueur n’en a pas encore.

10.  Défi : la corne de la licorne

Imaginez cette scène dans le jeu d’aventure :

Notre héros, Estragon, arrive dans un pub connu sous le nom de La corne de la licorne. Le patron lui demande : « Avez-vous besoin de faire boire votre monture ? »

— Non, répond Estragon, je n’ai pas de monture. Mais j’ai 50 pièces d’or, et je voudrais boire un verre.

— « Parfait ! » dit le patron. « J’ai de l’hydromel, du vin et du LaCroix. Que prendrez-vous ?

Pour ce défi, ajoutez en dessous de votre variable hasSteed les variables nécessaires pour la scène qui se passe au pub de La corne de la Licorne, en utilisant dans la mesure du possible l’inférence de type et les valeurs assignées. Ajoutez des variables pour stocker le nom du pub, le nom du patron qui sert à boire, le nombre de pièces d’or en possession du joueur.

Vous noterez que La corne de la licorne a une carte pour les boissons dans laquelle notre héros peut faire son choix. Quel type pourriez-vous utiliser pour représenter cette carte ? Si vous en avez besoin, consultez le tableau 2.1.

11.  Défi : le miroir magique

Désaltéré, Estragon est prêt pour une quête difficile. Êtes-vous dans les mêmes dispositions ?

Notre héros découvre un miroir magique qui montre au joueur le reflet de son nom (représenté par la variable playerName). En utilisant la magie du type String, transformez la chaîne playerName "Estragon" en "nogartsE", qui est la chaîne de caractères à l’envers.

Pour résoudre ce défi, consultez la documentation du type String à https://kotlinlang.org/api/latest/jvm/stdlib/kotlin/-string/. Fort heureusement, vous constaterez que les actions qu’un type particulier peut effectuer sont, pour un anglophone, généralement très significatives (indice : dans cette optique, le miroir magique a un côté renversant…).

3Conditions

Dans ce chapitre, vous allez apprendre à définir des règles pour contrôler l’exécution du code. Cette fonction du langage, qui est appelée contrôle du flux, permet de décrire les conditions dans lesquelles des parties spécifiques de votre programme doivent s’exécuter. Vous étudierez l’instruction if/else et l’expression when, et vous apprendrez à écrire des tests vrai/faux en utilisant les opérateurs de comparaison et les opérateurs logiques. Chemin faisant, vous jetterez également un coup d’œil à la fonctionnalité de modèle de chaîne de caractères de Kotlin.

Pour voir ces concepts en action, vous entamerez la création d’un projet appelé NyetHack, sur lequel vous allez travailler tout au long de ce livre.

D’où vient ce nom « NyetHack » ? Nous sommes ravis que vous ayez posé la question. Peut-être vous souvenez-vous de NetHack, un jeu créé par la NetHack DevTeam et sorti en 1987. NetHack était un jeu en mode texte qui se déroulait dans un monde fantastique et qui comportait des graphismes élaborés à l’aide de caractères ASCII (vous pouvez vérifier à www.nethack.org). Vous allez créer des éléments d’un jeu similaire en mode texte (nous sommes désolés, mais il n’y aura pas de graphismes ASCII). JetBrains, le créateur du langage Kotlin, ayant des bureaux en Russie, lorsque vous mélangez le mot « NetHack » et les origines russes de Kotlin, cela donne NyetHack.

1.  Instruction If/else