Skip to content

2. Installation de l'environnement de travail

L'environnement de travail du développeur Kotlin comprend trois éléments indépendants :

  • le JDK (« Java Development Kit ») : Kotlin, tel que nous l'utilisons dans ce document, est compilé en bytecode et exécuté par la machine virtuelle Java (JVM), exactement comme un programme Java. Le JDK fournit cette machine virtuelle, la commande [java] qui exécute les programmes, les bibliothèques standard de Java SE (que Kotlin utilise directement : fichiers, réseau, dates, threads…) et des outils comme [javap] ou [jar] ;
  • un outil de construction des projets, ici Gradle : il télécharge le compilateur Kotlin et les bibliothèques dont un projet a besoin, compile le projet, exécute ses tests et le met en paquet ;
  • un éditeur ou un IDE (environnement de développement intégré) : [IntelliJ IDEA], conçu par la société qui a créé Kotlin, est le choix naturel ; [VS Code] avec l'extension Kotlin est une autre possibilité. L'IDE s'appuie sur le JDK et sait lire les projets Gradle.

Le compilateur Kotlin lui-même ne fait pas partie de cette liste : Gradle le télécharge pour chaque projet, et IntelliJ IDEA l'embarque. On peut néanmoins l'installer à part, sous la forme de la commande [kotlinc], pour compiler un fichier isolé : nous le ferons pour notre premier programme, mais c'est facultatif.

Ce document utilise les commandes [java], [kotlinc] et [gradlew] pour toutes les opérations : elles fonctionnent de la même façon sous Windows, Linux et macOS, et quel que soit l'éditeur choisi.

2.1. Le JDK 25

2.1.1. Installation

Il existe plusieurs distributions du JDK, toutes construites à partir du code d'OpenJDK et équivalentes pour notre usage. Nous conseillons Eclipse Temurin, distribution libre et gratuite de la fondation Eclipse, qui se télécharge à l'adresse https://adoptium.net : on y choisit la version 25 - LTS, le système d'exploitation et le type de paquet (sous Windows, un installateur [.msi]). Sous Windows, on peut aussi l'installer depuis un terminal (PowerShell ou invite de commandes) avec le gestionnaire de paquets [winget] :

winget install EclipseAdoptium.Temurin.25.JDK

Il faut choisir le JDK (qui contient tout) et non le JRE seul (qui ne permet que d'exécuter des programmes déjà compilés). Lors de l'installation sous Windows, on coche les options qui ajoutent le JDK au [PATH] et qui définissent la variable d'environnement [JAVA_HOME] : Gradle, le compilateur [kotlinc] et les IDE s'en servent pour trouver le JDK. Sous Linux, le JDK est disponible dans les dépôts des principales distributions (par exemple [sudo apt install openjdk-25-jdk] sous Ubuntu).

Après l'installation, on vérifie dans un nouveau terminal que la commande [java] est disponible et donne la version du JDK :

1
2
3
4
> java --version
openjdk 25.0.4 2026-07-21 LTS
OpenJDK Runtime Environment Temurin-25.0.4+7 (build 25.0.4+7-LTS)
OpenJDK 64-Bit Server VM Temurin-25.0.4+7 (build 25.0.4+7-LTS, mixed mode, sharing)

Ces résultats ont été obtenus avec le JDK Temurin 25.0.4 utilisé pour écrire ce document. La version exacte dépend de la date de l'installation : les mises à jour correctives paraissent chaque trimestre. Tous les JDK 25.0.x conviennent pour ce document. Plusieurs JDK peuvent cohabiter sur un même poste : c'est le premier trouvé dans le [PATH] qui répond à la commande [java].

Le JDK contient aussi le compilateur Java [javac] ([javac --version] affiche [javac 25.0.4]). Nous n'en aurons pas besoin : le compilateur Kotlin produit lui-même le bytecode. Mais il est là, et un projet Gradle peut mêler sans difficulté des fichiers Kotlin et des fichiers Java, qui s'utilisent mutuellement.

2.1.2. Les commandes du JDK

Voici les commandes du JDK que nous utiliserons :

Commande
Rôle
[java -jar programme.jar]
exécute le programme contenu dans une archive [.jar] dont le manifeste désigne la classe principale
[java -cp classes Classe]
exécute la fonction [main] d'une classe compilée, cherchée dans le chemin de classes ([-cp], « class path »)
[javap -cp classes Classe]
affiche les membres d'une classe compilée : nous l'utiliserons pour voir ce que le compilateur Kotlin a produit
[jar]
crée ou lit une archive [.jar] (un fichier zip de classes compilées)

Le JDK fournit aussi l'interpréteur interactif [jshell], mais il n'accepte que des instructions Java. Pour essayer une expression Kotlin sans écrire de programme, on utilise plutôt les outils d'IntelliJ IDEA présentés plus loin.

2.2. Le compilateur Kotlin en ligne de commande

2.2.1. Installation

Cette installation est facultative : les projets Gradle n'en ont pas besoin, et IntelliJ IDEA embarque son propre compilateur. Elle permet de compiler et d'exécuter un fichier Kotlin isolé depuis un terminal, sans créer de projet.

Le compilateur se télécharge sur la page des versions publiées du projet Kotlin, sur GitHub (https://github.com/JetBrains/kotlin/releases) : dans la rubrique « Assets » de la version 2.4.20, on prend l'archive [kotlin-compiler-2.4.20.zip]. On la décompresse dans un dossier, par exemple [C:\Program Files\kotlinc], et on ajoute son sous-dossier [bin] au [PATH] (Windows : « Modifier les variables d'environnement système »). On peut aussi passer par un gestionnaire de paquets : [scoop install kotlin] sous Windows (avec Scoop), ou SDKMAN! sous Linux et macOS ([sdk install kotlin 2.4.20]). Le compilateur a besoin d'un JDK, qu'il trouve par la variable [JAVA_HOME] ou le [PATH].

On vérifie l'installation dans un nouveau terminal :

> kotlinc -version
info: kotlinc-jvm 2.4.20 (JRE 25.0.4+7-LTS)

La version du compilateur (2.4.20) est suivie de celle de la JVM qui l'exécute (le JDK 25).

2.2.2. Les commandes du compilateur

Le dossier [bin] contient deux commandes principales, [kotlinc] (le compilateur) et [kotlinr] (le lanceur, « runner ») :

Commande
Rôle
[kotlinc Fichier.kt -d classes]
compile un ou plusieurs fichiers source et range les fichiers [.class] produits dans le dossier [classes]
[kotlinc Fichier.kt -include-runtime -d prog.jar]
compile et produit une archive [.jar] exécutable qui contient aussi la bibliothèque standard de Kotlin
[kotlinr -cp classes Classe]
comme [java -cp classes Classe], en ajoutant au chemin de classes la bibliothèque standard de Kotlin
[kotlinr script.main.kts]
exécute un script Kotlin (fichier [.main.kts]), sans étape de compilation visible

Un script Kotlin est une suite d'instructions exécutées de haut en bas, sans fonction [main]. Par exemple, le script [essai.main.kts] suivant :

val s = "Kotlin " + 2.4
println(s.length)

s'exécute ainsi :

> kotlinr essai.main.kts
10

Avant Kotlin 2.4.20, le lanceur s'appelait [kotlin] ; il a été renommé [kotlinr] dans Kotlin 2.4.20, pour éviter un conflit de nom avec une autre commande [kotlin], celle de l'outil « Kotlin Toolchain ». L'ancien nom fonctionne encore dans la version 2.4.20 du compilateur (il n'est pas officiellement déprécié), mais la documentation de Kotlin recommande désormais [kotlinr].

Sans argument, la commande [kotlinc] lançait autrefois un interpréteur interactif (une « REPL », l'équivalent de [jshell]). Celui-ci est aujourd'hui abandonné :

> kotlinc
error: Kotlin REPL is deprecated and should be enabled explicitly for now; please use the '-Xrepl' option

Pour essayer quelques lignes de Kotlin, on se sert plutôt d'un fichier brouillon d'IntelliJ IDEA (« File / New / Scratch File », langage Kotlin), dont les résultats s'affichent à côté de chaque ligne.

2.3. Gradle

2.3.1. Le rôle de Gradle

Une application réelle utilise des bibliothèques qui ne font pas partie du JDK ni de la bibliothèque standard de Kotlin : un pilote de base de données, un cadriciel de tests, Hibernate, Spring, les coroutines… Elles sont distribuées sous forme d'archives [.jar] par un dépôt public, Maven Central (https://central.sonatype.com). Un outil de construction lit dans la description d'un projet les bibliothèques dont il a besoin, les télécharge (avec les bibliothèques dont elles dépendent elles-mêmes), puis compile, teste et met en paquet le projet.

Les deux grands outils de construction du monde Java sont Maven (utilisé par le cours Java dont ce document est l'adaptation) et Gradle. Kotlin peut être utilisé avec les deux, mais Gradle est l'outil de référence de l'écosystème Kotlin : c'est lui qu'utilisent Android et les projets Kotlin créés par IntelliJ IDEA, et ses fichiers de construction s'écrivent... en Kotlin. Ce document utilise Gradle 9.8.

2.3.2. Le wrapper Gradle : rien à installer

Il est inutile d'installer Gradle. Chaque projet de ce document est livré avec le wrapper Gradle (« enveloppe »), un petit lanceur formé de trois éléments placés à la racine du projet :

  • [gradlew] : le script de lancement pour Linux et macOS ([./gradlew]) ;
  • [gradlew.bat] : le même script pour Windows ([gradlew]) ;
  • le dossier [gradle/wrapper], qui contient le programme du lanceur ([gradle-wrapper.jar]) et sa configuration ([gradle-wrapper.properties]).

Le fichier [gradle-wrapper.properties] indique la version de Gradle à utiliser :

distributionUrl=https\://services.gradle.org/distributions/gradle-9.8.0-bin.zip

À la première utilisation, [gradlew] télécharge cette version de Gradle et la conserve sur le poste ; les fois suivantes, il l'utilise directement. Chaque projet utilise ainsi la version de Gradle avec laquelle il a été écrit, quelle que soit celle éventuellement installée sur le poste. Gradle a seulement besoin d'un JDK pour s'exécuter, qu'il trouve par la variable [JAVA_HOME] ou le [PATH].

Dans ce document, les commandes sont écrites sous la forme Windows ([gradlew build]) ; sous Linux ou macOS, on écrit [./gradlew build]. Dans PowerShell, il faut aussi écrire [.\gradlew build].

Si l'on veut malgré tout disposer de la commande [gradle] (pour créer un projet avec [gradle init], par exemple), on télécharge l'archive [gradle-9.8.0-bin.zip] sur https://gradle.org/releases, on la décompresse et on ajoute son sous-dossier [bin] au [PATH] ; ou on utilise [scoop install gradle], [sdk install gradle]. On vérifie alors l'installation :

> gradle --version
------------------------------------------------------------
Gradle 9.8.0
------------------------------------------------------------

Build time:    2026-09-24 13:40:00 UTC
Revision:      a927be5e08efe79e0b87ada06c762dde6bb9f8b8

Kotlin:        2.4.10
Groovy:        4.0.33
Ant:           Apache Ant(TM) version 1.10.17 compiled on April 6 2026
Launcher JVM:  25.0.4 (Eclipse Adoptium 25.0.4+7-LTS)
Daemon JVM:    /home/claude/tools/jdk-25.0.4+7 (no Daemon JVM specified, using current Java home)
OS:            Linux 6.18.44-fc-v64 amd64

Ces résultats ont été obtenus sous Linux. La ligne [Kotlin:] donne la version du Kotlin intégré à Gradle, qui sert à lire les fichiers de construction [.gradle.kts] : elle est indépendante de la version du compilateur Kotlin utilisée pour compiler nos programmes (2.4.20), qui est fixée par chaque projet. La ligne [Launcher JVM] donne le JDK qui exécute Gradle.

2.3.3. Les commandes Gradle

La commande [gradlew] est suivie d'une ou plusieurs tâches (« tasks ») ; on la tape dans le dossier racine du projet, celui qui contient le fichier [settings.gradle.kts] :

Commande
Rôle
[gradlew build]
compile les sources de [src/main/kotlin], exécute les tests et crée l'archive [.jar] du projet dans [build/libs]
[gradlew run]
compile, puis exécute la classe principale du projet (plugin [application])
[gradlew test]
compile, puis exécute les tests unitaires de [src/test/kotlin] (chapitre 6)
[gradlew clean]
supprime le dossier [build]
[gradlew tasks]
affiche la liste des tâches disponibles dans le projet
[gradlew installDist]
crée dans [build/install] une version installable du programme : ses archives et des scripts de lancement
[gradlew bootRun]
exécute une application Spring Boot (chapitre 13)

Les tâches dépendent les unes des autres : demander [run], c'est demander aussi la compilation, et Gradle ne refait que ce qui a changé depuis la construction précédente. L'option [-q] (« quiet ») supprime les messages de Gradle et ne laisse que ceux du programme exécuté ; l'option [-Pnom=valeur] définit une propriété que le fichier de construction peut lire : nous l'utiliserons pour choisir le programme à exécuter. Les arguments du programme lancé par [run] se passent par l'option [--args="a b"].

La première commande lancée dans un projet est lente : Gradle démarre un processus en arrière-plan (le « démon » Gradle), qui reste actif quelques heures pour accélérer les commandes suivantes, et télécharge ce dont le projet a besoin.

2.3.4. Maven Central et le cache de Gradle

Les bibliothèques téléchargées par Gradle sont conservées sur le poste, dans le cache de Gradle, le dossier [%USERPROFILE%\.gradle\caches] (Linux, macOS : [~/.gradle/caches]). Elles ne sont téléchargées qu'une fois et servent ensuite à tous les projets. Il faut donc un accès à Internet lors de la première construction d'un projet. Le même dossier [.gradle] contient, dans [wrapper/dists], les versions de Gradle téléchargées par le wrapper.

Une bibliothèque est désignée par ses coordonnées, trois noms séparés par deux-points : le groupe (souvent un nom de domaine inversé comme [org.hibernate.orm]), le nom de la bibliothèque et sa version, par exemple [tools.jackson.core:jackson-databind:3.1.5]. Ce sont ces coordonnées que l'on écrit dans le fichier de construction d'un projet, et que l'on trouve en cherchant la bibliothèque sur le site de Maven Central.

2.3.5. La syntaxe Kotlin DSL

Le fichier de construction d'un projet s'appelle [build.gradle.kts]. L'extension [.kts] indique qu'il s'agit d'un script Kotlin : le fichier est écrit dans un langage dédié (« DSL », « Domain Specific Language ») construit avec la syntaxe de Kotlin. Gradle accepte aussi des fichiers [build.gradle] écrits en Groovy, que l'on rencontre dans les projets plus anciens ; depuis Gradle 8.2, le Kotlin DSL est le format par défaut. Voici un extrait typique :

1
2
3
4
5
6
7
8
plugins {
    kotlin("jvm") version "2.4.20"
    application
}

dependencies {
    implementation("tools.jackson.core:jackson-databind:3.1.5")
}

Sans connaître encore Kotlin, on peut lire ce texte comme une configuration :

  • un nom suivi d'un bloc entre accolades ([plugins { ... }], [dependencies { ... }]) est une section de la configuration. En Kotlin, c'est l'appel d'une fonction qui reçoit en paramètre un bloc de code (une lambda, chapitre 7) ;
  • [kotlin("jvm")], [implementation("...")] sont des appels de fonctions ; les chaînes de caractères sont entre guillemets doubles ;
  • [version = "1.0"] affecte une valeur à une propriété du projet.

Un avantage de ce format est qu'IntelliJ IDEA le comprend comme du code Kotlin : il propose la complétion et signale les erreurs pendant la frappe.

2.4. L'éditeur

2.4.1. IntelliJ IDEA

IntelliJ IDEA, de la société JetBrains, est l'IDE recommandé pour ce document. JetBrains est aussi l'entreprise qui a conçu Kotlin (la première version stable du langage date de 2016) et qui continue de le développer : la prise en charge de Kotlin est intégrée à IntelliJ IDEA, sans extension à ajouter, et c'est celle qui suit au plus près les évolutions du langage. Ses fonctions de base, qui suffisent largement pour ce document, sont gratuites.

IntelliJ IDEA reconnaît les projets Gradle : « File / Open », puis on choisit le dossier qui contient le fichier [settings.gradle.kts] ; IntelliJ lance Gradle pour lire le projet (on peut suivre l'opération dans la fenêtre « Build »), télécharge les bibliothèques, puis affiche une vue « Gradle » qui donne accès à toutes les tâches du projet. Il permet ensuite d'exécuter un programme par le triangle vert placé dans la marge à côté de sa fonction [main] ([Ctrl-Maj-F10]), ou un test par le même triangle à côté de la fonction de test. On indique le JDK à utiliser dans « File / Project Structure / SDK » ; pour un projet Gradle, c'est en principe la déclaration [jvmToolchain(25)] du projet (voir plus loin) qui désigne le JDK de compilation.

IntelliJ IDEA offre aussi des aides propres à Kotlin : conversion automatique d'un fichier Java en Kotlin (« Code / Convert Java File to Kotlin File », ou simplement en collant du code Java dans un fichier Kotlin), affichage du bytecode produit par le compilateur (« Tools / Kotlin / Show Kotlin Bytecode »), et les fichiers brouillons déjà cités.

2.4.2. VS Code

VS Code est un éditeur gratuit et multiplateforme. Pour programmer en Kotlin, on lui ajoute l'extension Kotlin publiée par JetBrains (menu « Extensions », rechercher « Kotlin » et choisir celle dont l'éditeur est JetBrains). Cette extension officielle est récente : elle s'appuie sur le même moteur d'analyse du code qu'IntelliJ IDEA et sait importer les projets Gradle, mais elle n'offre pas encore tout le confort de l'IDE de JetBrains, et ses fonctions évoluent d'une version à l'autre. On consultera la page de l'extension pour savoir ce qu'elle prend en charge au moment où on l'installe. Pour ce document, elle convient pour lire et modifier les programmes ; on peut toujours les exécuter depuis le terminal intégré (« Terminal / New Terminal ») avec les commandes [gradlew] données dans le texte.

On ouvre dans VS Code le dossier d'un chapitre (« File / Open Folder »), par exemple le dossier [kotlin-oct-2026/03-bases].

2.4.3. Android Studio

Kotlin est aussi le langage recommandé par Google pour les applications Android. L'IDE Android Studio, construit sur IntelliJ IDEA, est réservé à ce type d'applications, dont ce document ne traite pas.

Dans la suite du document, les opérations sont décrites avec les commandes [java], [kotlinc] et [gradlew], qui donnent les mêmes résultats quel que soit l'éditeur.

2.5. Un premier programme : le fichier Hello.kt

2.5.1. Le programme

Un programme Kotlin peut être un simple fichier contenant une fonction [main], sans déclaration de classe. Créons le fichier [Hello.kt] suivant (dossier [02-installation] des exemples) :

1
2
3
4
// premier programme Kotlin : une fonction main de haut niveau
fun main() {
    println("1er essai avec Kotlin 2.4")
}

Commentaires :

  • ligne 1 : un commentaire. Tout ce qui suit [//] jusqu'à la fin de la ligne est ignoré par le compilateur ;
  • ligne 2 : la fonction [main], point d'entrée du programme. Le mot clé [fun] introduit une fonction. Elle est déclarée au niveau du fichier (« top-level function »), en dehors de toute classe ; elle n'a pas besoin d'être déclarée [public] (c'est la visibilité par défaut en Kotlin) ni de recevoir les arguments du programme ;
  • ligne 3 : la seule instruction du programme : elle écrit une ligne de texte à l'écran. La fonction [println] (et [print], qui ne passe pas à la ligne) fait partie de la bibliothèque standard de Kotlin ; c'est l'équivalent de [System.out.println] en Java. On remarquera l'absence de point-virgule à la fin de l'instruction : il est facultatif en Kotlin et on ne l'écrit pas.

Le programme est un peu plus simple que son équivalent dans le cours Java ([void main() { IO.println(...); }]), mais la vraie différence est ailleurs : la fonction [main] de haut niveau existe en Kotlin depuis sa première version, alors que Java ne l'accepte que depuis Java 25.

2.5.2. Compilation et exécution

On compile le programme avec [kotlinc], en demandant une archive [.jar] exécutable, puis on l'exécute avec la commande [java] du JDK :

1
2
3
> kotlinc Hello.kt -include-runtime -d hello.jar
> java -jar hello.jar
1er essai avec Kotlin 2.4
  • [-d hello.jar] : la destination de la compilation. Si elle se termine par [.jar], le compilateur produit une archive ; sinon, c'est un dossier dans lequel il range les fichiers [.class] ;
  • [-include-runtime] : l'archive contient aussi la bibliothèque standard de Kotlin ([kotlin-stdlib]), dont les programmes Kotlin ont besoin à l'exécution, et un manifeste qui désigne la classe principale. Le programme peut ainsi être exécuté par [java -jar] sur n'importe quel poste muni d'un JDK (ou d'un JRE), sans que Kotlin y soit installé. L'archive pèse en contrepartie plus de 5 Mo, essentiellement la bibliothèque standard ;
  • la compilation prend quelques secondes : le compilateur Kotlin, lui-même un programme Java, doit d'abord démarrer. Gradle et IntelliJ IDEA évitent ce coût en gardant un compilateur actif entre deux compilations.

Il n'existe pas en Kotlin d'équivalent de la commande [java Hello.java] de Java, qui compile et exécute en une seule étape un fichier source ; le plus proche est le script [.main.kts] présenté plus haut.

2.5.3. Ce que produit le compilateur

La machine virtuelle Java ne connaît que des classes : une fonction ne peut pas exister en dehors d'une classe. Le compilateur Kotlin enveloppe donc les fonctions de haut niveau d'un fichier dans une classe qu'il génère, et qu'il nomme d'après le fichier : le nom du fichier, suivi de [Kt]. Pour [Hello.kt], la classe générée est [HelloKt]. La commande [javap] du JDK affiche ses membres :

1
2
3
4
5
6
> javap -cp hello.jar HelloKt
Compiled from "Hello.kt"
public final class HelloKt {
  public static final void main();
  public static void main(java.lang.String[]);
}
  • la classe [HelloKt] est [public final] ;
  • elle contient notre fonction [main], devenue une méthode statique sans paramètre ;
  • et une seconde méthode [main(String[])], ajoutée par le compilateur : c'est la méthode [public static void main(String[] args)] que la machine virtuelle sait lancer. Elle se contente d'appeler la première. Nous reviendrons sur ce mécanisme au chapitre 3.

C'est ce nom de classe qui désigne le programme à exécuter : le manifeste de [hello.jar] contient la ligne [Main-Class: HelloKt], et l'on peut aussi écrire [java -cp hello.jar HelloKt].

Contrairement à Java, le nom du fichier n'est pas contraint : il n'a pas à être un identifiant valide ni à correspondre au nom d'une classe qu'il contient. Un fichier [premier-essai.kt] est accepté, et sa classe générée s'appelle [Premier_essaiKt] (le compilateur met une majuscule à la première lettre et remplace les caractères interdits dans un identifiant par [_]). Par convention, on donne néanmoins aux fichiers des noms commençant par une majuscule, sans tiret, comme [Hello.kt] ou [Bases01.kt].

Sous Windows, les caractères accentués écrits par [println] s'affichent correctement dans le terminal : la machine virtuelle convertit le texte dans l'encodage du terminal. Les fichiers source doivent être enregistrés en UTF-8, l'encodage par défaut de VS Code et d'IntelliJ IDEA, et celui que le compilateur Kotlin suppose toujours. Il en va autrement lorsque le programme est lancé par Gradle ([gradlew run], ou le triangle vert d'IntelliJ IDEA dans un projet Gradle) : sa sortie n'est plus une console mais un tube vers Gradle, et la machine virtuelle l'encode alors dans l'encodage historique de Windows (windows-1252), si bien qu'IntelliJ IDEA, qui attend de l'UTF-8, affiche un caractère de remplacement (un losange contenant un point d'interrogation) à la place de chaque caractère accentué. Les projets du cours règlent ce point dans leur fichier [build.gradle.kts] (section suivante). Pour le terminal intégré d'IntelliJ IDEA (PowerShell), on peut de même passer la console en UTF-8 en tapant [chcp 65001] au début de la session, ou une fois pour toutes en indiquant comme interpréteur, dans « File / Settings / Tools / Terminal / Shell path » : [powershell.exe -NoExit -Command "[Console]::OutputEncoding = [Text.Encoding]::UTF8"].

La commande [kotlinc] convient aux programmes d'un seul fichier qui n'utilisent que la bibliothèque standard. Dès qu'un programme a besoin d'une bibliothèque extérieure, il faudrait la télécharger, avec les bibliothèques dont elle dépend, et la passer au compilateur puis à la machine virtuelle : on écrit alors un projet Gradle. C'est ce que nous ferons pour tous les exemples du document.

2.6. Un projet Gradle

2.6.1. Structure du projet

Un projet Gradle est un dossier contenant un fichier de construction [build.gradle.kts] et des fichiers source rangés selon une arborescence standard. La racine d'une construction Gradle contient en plus un fichier [settings.gradle.kts], qui donne son nom à la construction et la liste des projets qu'elle regroupe, et le wrapper. Le dossier [02-installation] des exemples est une construction qui regroupe deux projets, [PremierProgramme] et [DeuxMains] :

02-installation/
├── gradlew
├── gradlew.bat
├── gradle/
│   └── wrapper/
│       ├── gradle-wrapper.jar
│       └── gradle-wrapper.properties
├── settings.gradle.kts
├── build.gradle.kts
├── Hello.kt
├── PremierProgramme/
│   ├── build.gradle.kts
│   └── src/
│       └── main/
│           └── kotlin/
│               └── premier/
│                   └── Main.kt
└── DeuxMains/
    ├── build.gradle.kts
    └── src/
        └── main/
            └── kotlin/
                └── deuxmains/
                    ├── P1.kt
                    └── P2.kt
  • [src/main/kotlin] contient les fichiers source du programme ; [src/main/resources] contiendrait les fichiers de données ou de configuration à placer à côté des classes compilées ; [src/test/kotlin] contiendrait les tests (chapitre 6) ;
  • sous [src/main/kotlin], l'arborescence des dossiers suit celle des paquetages (chapitre 4) : le fichier [Main.kt] du paquetage [premier] est dans le dossier [premier]. Kotlin n'impose pas cette correspondance, contrairement à Java, mais la recommande, et nous la respecterons toujours ;
  • le fichier [Hello.kt] n'appartient à aucun projet : il est placé là pour être compilé par [kotlinc], et Gradle l'ignore.

Un projet isolé (non regroupé avec d'autres) a la même structure, avec ses propres fichiers [settings.gradle.kts] et wrapper : c'est le cas du projet du chapitre 3. On peut créer cette arborescence à la main, ou laisser l'IDE la créer (IntelliJ : « File / New / Project », choisir « Kotlin » comme langage et « Gradle » comme système de construction, avec le Kotlin DSL).

Nous verrons à la section suivante les fichiers de la racine. Le fichier [build.gradle.kts] du projet [PremierProgramme] est le suivant :

// le projet [PremierProgramme] : un programme Kotlin exécuté par la JVM
plugins {
    // le plugin Kotlin pour la JVM (sa version est déclarée dans le projet racine)
    kotlin("jvm")
    // le plugin [application] : tâches run et installDist
    application
}

// les coordonnées du projet
group = "exemples.installation"
version = "1.0"

// le dépôt où sont cherchées les bibliothèques
repositories {
    mavenCentral()
}

dependencies {
    // aucune bibliothèque en dehors de la bibliothèque standard de Kotlin, ajoutée par le plugin
}

kotlin {
    // le code est compilé et exécuté avec le JDK 25
    jvmToolchain(25)
    compilerOptions {
        // un avertissement du compilateur est traité comme une erreur
        allWarningsAsErrors = true
        // les noms des paramètres des fonctions sont conservés dans le bytecode
        javaParameters = true
    }
}

application {
    // la classe qui contient la fonction main exécutée par [gradlew run]
    mainClass = "premier.MainKt"
}

// sous Windows, les sorties écran du programme lancé par Gradle (run, bootRun...) sont encodées en UTF-8,
// sinon les caractères accentués sont abîmés dans la console d'IntelliJ IDEA ou le terminal
tasks.withType<JavaExec>().configureEach {
    jvmArgs("-Dstdout.encoding=UTF-8", "-Dstderr.encoding=UTF-8")
}

Commentaires :

  • ligne 1 : un commentaire. Le fichier de construction est du Kotlin : les commentaires s'y écrivent comme dans un programme ;
  • lignes 2-7 : les plugins (« greffons ») appliqués au projet. Ce sont eux qui donnent à Gradle les tâches dont nous avons besoin :
    • ligne 4 : le plugin Kotlin pour la JVM. Il ajoute les tâches qui compilent les fichiers de [src/main/kotlin] et ajoute automatiquement la bibliothèque standard de Kotlin aux dépendances du projet. La notation [kotlin("jvm")] est un raccourci pour l'identifiant complet du plugin, [org.jetbrains.kotlin.jvm]. Sa version n'est pas indiquée ici, parce que le projet fait partie d'une construction de plusieurs projets dont la racine fixe cette version pour tous (section suivante). Dans un projet isolé, on écrit [kotlin("jvm") version "2.4.20"] : c'est cette version qui détermine la version du compilateur Kotlin utilisé ;
    • ligne 6 : le plugin [application], qui ajoute les tâches [run] (exécuter le programme) et [installDist] (préparer une version installable) ;
  • lignes 10-11 : les coordonnées du projet, groupe et version, qui l'identifient avec son nom ([PremierProgramme], le nom de son dossier). Ce sont aussi les coordonnées par lesquelles un autre projet pourrait désigner celui-ci, comme on désigne une bibliothèque de Maven Central (chapitre 6) ;
  • lignes 14-15 : le dépôt dans lequel Gradle cherche les bibliothèques du projet, ici Maven Central ;
  • lignes 18-20 : les bibliothèques utilisées par le projet. Ce projet n'en utilise aucune, en dehors de la bibliothèque standard de Kotlin que le plugin Kotlin ajoute de lui-même ;
  • ligne 24 : la chaîne d'outils (« toolchain ») du projet : le code est compilé pour Java 25 et exécuté par [gradlew run] avec un JDK 25, même si Gradle lui-même est exécuté par un autre JDK. Gradle cherche ce JDK parmi ceux qui sont installés sur le poste. S'il n'en trouve aucun, il échoue avec le message [Cannot find a Java installation on your machine ... matching: {languageVersion=25 ...}], sauf si la construction déclare le plugin [foojay-resolver-convention], ce que font tous les projets du cours (section suivante) : Gradle télécharge alors lui-même un JDK 25. Dans IntelliJ IDEA, on peut aussi installer ce JDK par « File / Project Structure / SDK / Add SDK / Download JDK » ;
  • ligne 27 : un avertissement du compilateur est traité comme une erreur et arrête la compilation. Le compilateur Kotlin signale beaucoup de maladresses (variable inutilisée, transtypage inutile, opérateur [!!] superflu…) : nous voulons des programmes qui n'en produisent aucune ;
  • ligne 29 : le compilateur conserve dans le bytecode les noms des paramètres des fonctions. Certaines bibliothèques s'en servent, par exemple Jackson pour créer un objet à partir d'un document JSON (chapitre 5) ou Spring (chapitre 13) ;
  • lignes 33-35 : la classe dont la fonction [main] est exécutée par [gradlew run]. Le nom est celui de la classe générée par le compilateur pour le fichier [Main.kt] du paquetage [premier] : [premier.MainKt] ;
  • lignes 38-41 : toutes les tâches qui exécutent un programme Java ([JavaExec]), comme [run], reçoivent deux options de la machine virtuelle : les sorties écran du programme ([System.out], [System.err]) sont encodées en UTF-8. Sans elles, les caractères accentués écrits par le programme seraient abîmés dans la console d'IntelliJ IDEA sous Windows (voir la remarque sur les accents, plus haut). [tasks.withType<JavaExec>()] désigne toutes les tâches de ce type, et [configureEach { … }] règle chacune d'elles. Tous les projets du cours qui exécutent un programme contiennent ce réglage.

Ce projet n'utilise aucune bibliothèque. Un projet qui en utilise une la déclare dans la section [dependencies] par ses coordonnées, comme nous le ferons à partir du chapitre 5 :

1
2
3
dependencies {
    implementation("tools.jackson.core:jackson-databind:3.1.5")
}

Le mot [implementation] indique que la bibliothèque est nécessaire pour compiler et pour exécuter le programme. On rencontrera aussi [runtimeOnly] (nécessaire seulement à l'exécution, comme un pilote de base de données) et [testImplementation] (nécessaire seulement aux tests). Gradle téléchargera alors [jackson-databind] et les bibliothèques dont elle dépend.

Le fichier [Main.kt] contient le programme :

1
2
3
4
5
6
package premier

// le programme principal du projet [PremierProgramme]
fun main() {
    println("1er essai avec un projet Gradle / Kotlin 2.4")
}
  • ligne 1 : le fichier appartient au paquetage [premier]. Le nom complet de la classe générée pour ce fichier est donc [premier.MainKt] ;
  • lignes 4-6 : la fonction [main], sous la même forme que dans [Hello.kt]. Un fichier Kotlin peut contenir un nombre quelconque de fonctions et de classes, publiques ou non : le nom du fichier ne dépend pas de son contenu.

2.6.2. Compilation et exécution

La commande [gradlew build], tapée dans le dossier [02-installation], compile les projets de la construction (et exécuterait leurs tests s'ils en avaient) : Gradle télécharge si nécessaire le plugin Kotlin, le compilateur et la bibliothèque standard, puis compile les fichiers de [src/main/kotlin] et range les classes produites dans le dossier [build/classes/kotlin/main] de chaque projet, en respectant l'arborescence des paquetages ([PremierProgramme/build/classes/kotlin/main/premier/MainKt.class]).

La commande [gradlew run] compile un projet puis l'exécute. Dans une construction de plusieurs projets, on désigne le projet par son chemin, précédé de deux-points : [:PremierProgramme:run] est la tâche [run] du projet [PremierProgramme]. Avec l'option [-q], on ne voit plus que l'affichage du programme :

> gradlew -q :PremierProgramme:run
1er essai avec un projet Gradle / Kotlin 2.4

On peut aussi se placer dans le dossier du projet et taper [..\gradlew -q run] : Gradle remonte jusqu'au fichier [settings.gradle.kts] de la racine et exécute la tâche [run] du projet du dossier courant. En revanche, [gradlew run] tapé à la racine exécuterait la tâche [run] de tous les projets qui en ont une.

Une fois le projet compilé, on peut aussi exécuter la classe directement avec la commande [java], en lui indiquant où se trouvent les classes (dans le dossier [PremierProgramme]) :

> java -cp build/classes/kotlin/main premier.MainKt
1er essai avec un projet Gradle / Kotlin 2.4

Cela fonctionne ici parce que ce programme n'utilise en fait rien de la bibliothèque standard de Kotlin à l'exécution : le compilateur remplace l'appel de [println] par un appel direct de [System.out.println]. Mais un programme qui utilise, par exemple, une liste ([listOf(1, 2, 3)]) échoue de cette façon avec l'erreur [java.lang.NoClassDefFoundError: kotlin/collections/CollectionsKt] : la classe [CollectionsKt] de la bibliothèque standard est introuvable. Il faut alors ajouter au chemin de classes l'archive [kotlin-stdlib-2.4.20.jar] (elle est dans le cache de Gradle ; le dossier [lib] du compilateur [kotlinc] la contient aussi, sous le nom [kotlin-stdlib.jar]) : [java -cp "build/classes/kotlin/main;chemin/kotlin-stdlib-2.4.20.jar" premier.MainKt] sous Windows (le séparateur est [:] sous Linux et macOS). La commande [kotlinr -cp build/classes/kotlin/main premier.MainKt] du compilateur en ligne de commande le fait pour nous.

Le plus simple est de demander à Gradle de rassembler le programme et tout ce dont il a besoin, avec la tâche [installDist] du plugin [application] :

> gradlew -q :PremierProgramme:installDist

Elle crée le dossier [PremierProgramme/build/install/PremierProgramme], qui contient un dossier [lib] (l'archive du projet, la bibliothèque standard de Kotlin et, le cas échéant, les autres bibliothèques du projet) et un dossier [bin] avec deux scripts de lancement, [PremierProgramme] (Linux, macOS) et [PremierProgramme.bat] (Windows), qui appellent [java] avec le bon chemin de classes. C'est sous cette forme, que l'on peut copier sur un autre poste muni d'un JDK, qu'un programme est livré :

> PremierProgramme\build\install\PremierProgramme\bin\PremierProgramme
1er essai avec un projet Gradle / Kotlin 2.4

Le dossier [build] peut être supprimé sans risque ([gradlew clean]) : il est recréé par la compilation suivante. La commande [gradlew build] produit aussi l'archive [build/libs/PremierProgramme-1.0.jar] du projet, qui ne contient que ses propres classes.

2.7. Un projet de plusieurs modules

2.7.1. La construction multi-projets

Une construction Gradle peut regrouper plusieurs projets, appelés ses sous-projets (ou modules). C'est le fichier [settings.gradle.kts] de la racine qui les énumère. Celui du dossier [02-installation] est le suivant :

// chapitre 2 : une construction Gradle qui regroupe les deux projets du dossier

// Gradle télécharge lui-même le JDK demandé par jvmToolchain(25) s'il ne le trouve pas sur le poste
plugins {
    id("org.gradle.toolchains.foojay-resolver-convention") version "1.0.0"
}

rootProject.name = "installation"

// les sous-projets, désignés par le nom de leur dossier
include("PremierProgramme", "DeuxMains")
  • lignes 3-6 : un plugin de la construction elle-même (et non d'un projet), [foojay-resolver-convention] : lorsqu'un projet demande par [jvmToolchain(25)] un JDK que Gradle ne trouve pas sur le poste, Gradle le télécharge depuis le service foojay.io et le range dans son cache. Le premier lancement est un peu plus long, mais l'étudiant n'a plus besoin d'avoir installé le bon JDK. Le bloc [plugins] doit être le premier du fichier. Toutes les constructions du cours déclarent ce plugin ;
  • ligne 8 : le nom de la construction (le projet racine). Sans cette ligne, ce serait le nom du dossier ;
  • ligne 11 : les sous-projets, désignés par le nom de leur dossier. Un sous-projet peut être rangé plus profondément : [include("ImpotsV5:impots-dao")] désigne le projet du dossier [ImpotsV5/impots-dao] (chapitre 6).

Le projet racine a lui aussi un fichier [build.gradle.kts] :

1
2
3
4
5
// chapitre 2 : projet racine. Le plugin Kotlin est déclaré ici, avec sa version, sans être appliqué :
// ce sont les sous-projets qui l'appliquent
plugins {
    kotlin("jvm") version "2.4.20" apply false
}
  • lignes 3-4 : le plugin Kotlin est déclaré, avec sa version, mais il n'est pas appliqué au projet racine ([apply false]), qui ne contient pas de code à compiler. Cette déclaration rend le plugin disponible pour les sous-projets, qui l'appliquent par [kotlin("jvm")] sans répéter la version : tous les sous-projets utilisent ainsi la même version de Kotlin, que l'on change en un seul endroit. Si chaque sous-projet déclarait lui-même la version du plugin, Gradle le chargerait plusieurs fois, ce que le plugin Kotlin déconseille.

Dans le dossier [02-installation], la commande [gradlew build] compile les deux projets ; un IDE qui ouvre ce dossier y voit les deux projets à la fois. Au chapitre 6, nous verrons qu'un sous-projet peut dépendre d'un autre ([implementation(project(":ImpotsV5:impots-dao"))]), et aux chapitres 10 à 13 qu'une construction peut même réutiliser les projets d'une autre construction ([includeBuild]).

2.7.2. Un projet avec plusieurs fonctions main

Un projet peut contenir plusieurs fichiers ayant chacun une fonction [main] : chacun est un point d'entrée possible, et c'est au moment de l'exécution que l'on choisit la classe à exécuter. Le projet [DeuxMains] contient deux fichiers du paquetage [deuxmains]. Le fichier [P1.kt] :

1
2
3
4
5
6
package deuxmains

// un premier point d'entrée du projet [DeuxMains]
fun main() {
    println("main du fichier P1.kt")
}

et le fichier [P2.kt], identique au nom près. Les deux fonctions s'appellent [main] et appartiennent au même paquetage : il n'y a pas de conflit, car elles sont placées dans deux classes différentes, [deuxmains.P1Kt] et [deuxmains.P2Kt]. La commande [java] exécute la classe qu'on lui désigne (dans le dossier [DeuxMains], une fois le projet compilé) :

1
2
3
4
> java -cp build/classes/kotlin/main deuxmains.P1Kt
main du fichier P1.kt
> java -cp build/classes/kotlin/main deuxmains.P2Kt
main du fichier P2.kt

Pour la commande [gradlew run], c'est le fichier [build.gradle.kts] du projet qui désigne la classe à exécuter :

1
2
3
4
5
6
application {
    // le programme exécuté par [gradlew run] est désigné par la propriété [main] ;
    // à défaut, c'est la fonction main du fichier P2.kt :
    // gradlew -q :DeuxMains:run -Pmain=deuxmains.P1Kt
    mainClass = providers.gradleProperty("main").orElse("deuxmains.P2Kt")
}
  • ligne 5 : la classe exécutée est désignée par la propriété [main] de Gradle, que l'on donne sur la ligne de commande par l'option [-Pmain=...]. La fonction [providers.gradleProperty("main")] lit cette propriété ; [orElse(...)] donne la valeur à utiliser si elle n'a pas été donnée : ici, la classe [deuxmains.P2Kt].

Le reste du fichier reprend celui de [PremierProgramme], avec en plus un réglage de la tâche [run] ([standardInput = System.in]) qui transmet au programme ce que l'on tape au clavier : sans lui, un programme lancé par [gradlew run] ne peut pas lire le clavier.

Résultats :

1
2
3
4
> gradlew -q :DeuxMains:run
main du fichier P2.kt
> gradlew -q :DeuxMains:run -Pmain=deuxmains.P1Kt
main du fichier P1.kt

On choisit ainsi le programme à exécuter sans modifier le fichier [build.gradle.kts]. Avec IntelliJ IDEA, la question ne se pose pas : chaque fonction [main] a son propre triangle vert dans la marge.

2.7.3. Un paquetage par programme

Les projets des chapitres suivants utilisent tous ce mécanisme, car chacun contient de nombreux petits programmes. Pour que ces programmes ne se gênent pas, le document suit une convention simple : chaque programme a son propre paquetage, qui porte son nom en minuscules et est rangé dans le dossier du même nom. Ainsi, le programme [Bases04.kt] du chapitre 3 est le fichier [03-bases/src/main/kotlin/bases04/Bases04.kt], qui commence par la ligne [package bases04] ; sa classe est [bases04.Bases04Kt], et on l'exécute par :

gradlew -q run -Pmain=bases04.Bases04Kt

Cette convention a deux raisons :

  • tous les programmes d'un projet sont compilés ensemble. S'ils étaient dans le même paquetage, deux programmes ne pourraient pas déclarer une fonction ou une classe de même nom (une fonction [afficher], une classe [Personne]…) : le compilateur signalerait une déclaration en double. Avec un paquetage par programme, chacun a son propre espace de noms ;
  • un programme formé de plusieurs fichiers est simplement un paquetage de plusieurs fichiers : au chapitre 4, le paquetage [personnes] contient la classe [Personne] dans [Personne.kt] et le programme principal dans [Main.kt], exécuté par [-Pmain=personnes.MainKt].

Le texte de chaque chapitre donne, avant les résultats d'un programme, la commande exacte qui l'exécute. Nous reviendrons sur cette organisation au chapitre 3.

2.8. Les exemples du document

Pour exécuter les exemples du document :

  • on récupère le dossier [kotlin-oct-2026] (lien en début de document). Il contient un dossier par chapitre ([02-installation], [03-bases], [04-classes]…), et chacun de ces dossiers est une construction Gradle, avec son wrapper : un projet unique, ou plusieurs sous-projets regroupés par son fichier [settings.gradle.kts] ;
  • on ouvre le dossier d'un chapitre dans IntelliJ IDEA (« File / Open ») ; on exécute alors un programme par le triangle vert placé à côté de sa fonction [main] ;
  • ou bien, dans un terminal placé dans le dossier du chapitre, on exécute un programme par [gradlew -q run -Pmain=paquetage.ClasseKt] (avec, s'il y a plusieurs sous-projets, le chemin du sous-projet devant [run]) ; le texte de chaque chapitre donne la commande exacte.

La première commande [gradlew] d'un chapitre a besoin d'un accès à Internet : Gradle télécharge alors sa propre distribution, le plugin Kotlin et les bibliothèques du projet, qu'il conserve ensuite dans son cache.

Les chapitres 10 et 11 utilisent une base de données MySQL ; l'installation de MySQL avec [Laragon] est décrite en annexe.