> ## Content Index
> Fetch the complete content index at: https://www.sfeir.dev/llms.txt
> Use this file to discover other available public pages before exploring further.

# Kotlin 2.2 : toutes les nouveautés du langage
- URL: https://www.sfeir.dev/back/kotlin-2-2-toutes-les-nouveautes-du-langage/
- Published: 2025-12-02T14:44:38.000Z
- Updated: 2025-12-03T07:43:02.000Z
- Description: Kotlin 2.2.0 est sorti il y a quelques mois maintenant, mais peu d'articles résument les nouveautés qui ont enrichi le langage. Petit tour d'horizon de ces nouveautés.
- Author: Adelin Rommes
- Tags: Kotlin, Back

## **Guard conditions : des conditions multiples dans vos expressions `when`**

Les `guard conditions` permettent de définir plusieurs conditions dans les déclarations ou expressions `when`. Bon, ok, cela ne semble pas très intéressant dit comme ça. Reprenons depuis le début. 

En Kotlin, le mot-clé `when` est l'équivalent du `switch-case` en Java. Il peut être utilisé en déclaration pour exécuter un bloc de code de manière conditionnelle ou en expression pour attribuer une valeur à une variable (comme cela est possible aussi en Java depuis la version 13). Voici des exemples simples pour mettre en contexte :

```kotlin
fun temperatureFeeling(temperature: Int) = when (temperature) {
	
	// Remarquer qu'en Kotlin, il n'y a pas de 'break' keyword
	// car uniquement une branche est exécutée
	0 -> println("The water is becoming ice")
	
	// Les conditions des branches peuvent être variées
	in -50..-1 -> println("freezing cold")
	in 1..15 -> println("cold")
	in 35..60 -> println("super hot")
}

fun main() {

    // La fonction ne renvoie rien
	listOf(-50, 0, 5, 18, 45).forEach { temperatureFeeling(it) }
	
	// output
	// freezing cold
	// The water is becoming ice
	// cold
	// kinda cozy
	// super hot
}
```

`when` as a declaration

```kotlin
enum class TemperatureFeeling {
	FREEZING_COLD,
	THE_WATER_IS_BECOMING_ICE,
	COLD,
	KINDA_COZY,
	SUPER_HOT
}

fun setTemperature(temperature: TemperatureFeeling): Int = when (temperature) {
	TemperatureFeeling.FREEZING_COLD -> -50
	TemperatureFeeling.THE_WATER_IS_BECOMING_ICE -> 0
	TemperatureFeeling.COLD -> 5
	TemperatureFeeling.KINDA_COZY -> 22
	TemperatureFeeling.SUPER_HOT -> 50
}

fun main() {

    // Ici, la fonction renvoie un entier et affiche cette valeur
	TemperatureFeeling.entries.forEach { println(setTemperature(it)) }
	
	// output
	// -50
	// 0
	// 5
	// 22
	// 50
}
```

`when` as an expression

L'une des utilisations très pratiques du `when` en Kotlin est d'agir en fonction du type d'un objet :

```kotlin
// Une interface (ou classe) marquée comme 'sealed' ne peut être étendue
// uniquement par des classes connues lors de la compilation et définies dans le même package.
// Cela permet de contrôler les classes qui en héritent
// mais aussi de pouvoir être exhaustif dans l'utilisation la définition des branches d'un `when`.
sealed class Animal(val name: String)

data class Cat(val catName: String) : Animal(catName)
data class Fish(val fishName: String, val liveInOcean: Boolean) : Animal(fishName)

fun displayAnimalName(animal: Animal) = when(animal) {
	is Cat -> println("The cat's name is ${animal.name}")
	// Remarquer que le compilateur infère le type d'animal (liveInOcean est disponible)
	is Fish -> println("The fish's name is ${animal.name} and it lives in the ocean [${animal.liveInOcean}]")
}

fun main() {
	// listOf crée une liste immuable d'éléments
	// de type Animal, le type est inféré
	listOf(Cat("Fluffy"), Fish("Titanic", true), Fish("Nemo", false))
		// Pour chaque Animal défini, nous appelons la fonction
		.forEach { displayAnimalName(it) }

    // output
	// The cat's name is Fluffy
	// The fish's name is Titanic and it lives in the ocean [true]
	// The fish's name is Nemo and it lives in the ocean [false]
}
```

`when` for checking value type

Et nous y voilà ! Nous avons toutes les bases pour comprendre exactement ce que sont les `guard conditions`. Elles permettent d'inclure plus qu'une condition pour une branche du `when`.

```kotlin
sealed interface Vehicule

data class Car(val numberOfPassagers: Int) : Vehicule
data class Truck(val hasATrailer: Boolean) : Vehicule

fun displayCar(number: Int) = println("This is a car with $number passagers")
fun displayTruck() = println("This is a truck")

fun trailerOrPassagers(vehicule: Vehicule) = when(vehicule) {
	is Car -> displayCar(vehicule.numberOfPassagers)
    // Le guard est ici, après le 'if'
	is Truck if vehicule.hasATrailer -> displayTruck()
	else -> println("This is something else!")
}

fun main() {
	// listOf crée une liste immuable d'éléments
	// de type Vehicule, le type est inféré
	listOf(Car(4), Truck(true), Truck(false))
		// Pour chaque véhicule définit, nous appelons la fonction
		.forEach { trailerOrPassagers(it) }
	
	// output
	// This is a car with 4 passagers
	// This is a truck
	// This is something else!
}
```

guard conditions

Cela semble une utilisation fort spécifique. Et je ne pourrai pas vous contredire. Mais probablement que l'on verra d'autres usages avec les `RichError` annoncées lors de la KotlinConf'2025, qui devraient arriver avec Kotlin 2.4\. Cela permet donc de faire des choses beaucoup plus complexes mais de manière concise dans les conditions des différentes branches.

[KotlinConf′25 : quelles sont les annonces à retenir ?À la KotlinConf′25, JetBrains a levé le voile sur l’avenir de Kotlin : langage, IA, multiplateforme, outillage… Voici ce qu’il faut retenir.![](https://storage.ghost.io/c/0b/31/0b31478e-a66a-46f1-8c34-d44388e21192/content/images/icon/plume-point-318.png)sfeir.dev - Le média incontournable pour les passionnés de tech et d'intelligence artificielleAdelin Rommes![](https://storage.ghost.io/c/0b/31/0b31478e-a66a-46f1-8c34-d44388e21192/content/images/thumbnail/kotlin-conf-banner-1-1-1.png)](https://www.sfeir.dev/tendances/kotlinconf25-quelles-sont-les-annonces-a-retenir/)

## **Multi-dollar string interpolation : simplifiez vos interpolations multi-lignes**

Cette fonctionnalité est beaucoup plus facile à appréhender. L'interpolation d'une variable dans un string est très concise en Kotlin. Il suffit de précéder la variable d'un symbole `$` et éventuellement de l'entourer d'accolades s'il s'agit d'une expression. Pour afficher un dollar, on peut évidemment l'échapper avec un backslash. 

```kotlin
val name = "John"
println("Hi $name. Your name has ${name.length} characters")
println("I want to display a dollar \$")
```

escaped string

Il existe les strings multi-lignes. Ils ne contiennent aucun échappement et peuvent contenir des nouvelles lignes et tout autre caractère. Puisqu'il n'y a aucun échappement, afficher un symbole `$` n'était pas très pratique (→ `${'$'}`)

```kotlin
println("""
    I am a really long text
    and I need to be displayed
    on several lines
""")

println("""But it is annoying to display ${'$'}""")
```

multiline string

La nouvelle interpolation permet de résoudre ce souci. Elle permet de définir combien de symboles dollar consécutifs doivent apparaître pour déclencher une interpolation.

```kotlin
val name = "John"
println($$"""
    But it is not a problem anymore $ !!!
    What do you think, $$name ?
""")
```

multi-dollar interpolation

Cela est très pratique dans tous les cas où il faut utiliser littéralement un dollar.

## Non-local `break` and `continue` : plus de flexibilité dans vos boucles

 Jusqu'à maintenant, le code suivant ne compilait pas :

```kotlin
val numbers = listOf(1, 2, 3, 4, 5, 6, 7, 8)
for (number in numbers) {
    number.takeIf { it % 2 == 0 } // Retourne l'élément s'il satisfait la condition (sinon null)
        ?.let { // Lambda qui est exécutée si l'élément n'est pas null
            continue // compilation error
        }
    println(number)
}
```

before Kotlin 2.2.0

Ce bout de code devrait permettre de n'afficher que les nombres impairs, d'une façon très idiomatique. Mais on remarque que le compilateur n'accepte pas le `continue` à l'intérieur d'une lambda pour interagir avec la boucle qui l'entoure. Pour que ce bout de code fonctionne, il fallait oublier la façon idiomatique :

```kotlin
val numbers = listOf(1, 2, 3, 4, 5, 6, 7, 8)
for (number in numbers) {
    if (number % 2 == 0) {
        continue
    }
    println(number)
}
```

non idiomatic way

Bon, pour un exemple aussi simple, ce n'est pas dérangeant... mais pour des fonctions bien plus complexes, cela pourrait poser problème. Autre exemple 

```kotlin
@JvmInline
value class Element(val name: String) {
	fun filterLength(): String? = if (name.length == 3) null else name.reversed()
}

fun main() {	
	val elements = listOf(
		Element("foo"),
		Element("bar"),
		Element("John"),
		Element("Something else")
	)
	println(processElements(elements))
}

fun processElements(elements: List<Element>): Boolean {
	
	for (element in elements) {
		// filterLength peut renvoyer null
		val reversed = element.filterLength() ?: run {
			// la scoped function run permet d'exécuter un block de code
			// et permet généralement d'exécuter des side effects (log par exemple)
			println("Skipping '${element.name}'")
			continue // erreur de compilation ici
		}
		
		if (reversed == "nhoJ") return true
	}
	
	return false
}
```

continue in scoped function

Kotlin 2.2.0 corrige cela et il n'y a plus d'incohérence. Nous avons le comportement attendu : les `continue` et `break` définis dans les lambdas permettent d'interagir avec les boucles dans lesquelles ces lambdas sont appelées.

```kotlin
val numbers = listOf(1, 2, 3, 4, 5, 6, 7, 8)
for (number in numbers) {
    number.takeIf { it % 2 == 0 }?.let { continue }
    println(number)
}
```

non-local continue works as **expected*

Tout cela est aussi valable pour sortir d'une boucle avec `break`. En revanche, les non-local `break` and `continue` sont applicables uniquement pour les `inline functions`, c'est-à-dire les fonctions dont le corps est directement remplacé aux endroits où elles sont appelées lors de la compilation.

## Base64 : l'API d'encodage enfin en stable

Introduite en preview avec Kotlin 1.8.20, l'API `kotlin.io.encoding.Base64` est enfin stable ! Voici un exemple très basique d'utilisation.

```kotlin
import kotlin.io.encoding.Base64

fun main() {
	val encoded = Base64.encode("This string is not encoded".toByteArray())
	println(encoded) // SGVsbG8sIFdvcmxkIQ==
	
	val decoded = Base64.decode(encoded)
	println(decoded.decodeToString()) // This string is not encoded
}
```

Base64 API

## Comment migrer vers Kotlin 2.2

Rien de plus simple : il suffit de changer la version dans les fichiers des outils de build correspondants :

```kotlin
plugins {
    // Replace `<...>` with the plugin name appropriate for your target environment
    kotlin("<...>") version "2.2.20"
    // For example, if your target environment is JVM:
    // kotlin("jvm") version "2.2.20"
    // If your target is Kotlin Multiplatform:
    // kotlin("multiplatform") version "2.2.20"
}
```

Gradle: build.gradle.kts

```xml
<properties>
    <kotlin.version>2.2.20</kotlin.version>
</properties>

<plugins>
    <plugin>
        <groupId>org.jetbrains.kotlin</groupId>
        <artifactId>kotlin-maven-plugin</artifactId>
        <version>${kotlin.version}</version>
    </plugin>
</plugins>
```

Maven: pom.xml

## Fonctionnalités en preview

D'autres fonctionnalités sont disponibles en preview comme les *typealias* qui pourront être imbriqués dans des classes ou encore la *context-sensitive resolution*... mais gardons ça pour quand ça sera stable.

## Pour aller plus loin

Si vous avez envie de voir Sebastian, Alejandro et Michail présenter toutes les fonctionnalités de manière plus détaillée, c'est par ici que ça se passe :