⚠️ Gestion des exceptions
📖 Qu'est-ce qu'une exception ?
Une exception est une erreur qui se produit pendant l'exécution d'un programme. En gérant correctement les exceptions, vous assurez le fonctionnement stable de votre programme sans interruption !
💡 try-catch de base
Utilisation de base
fun main() {
try {
val result = 10 / 0 // Erreur générée !
println(result)
} catch (e: Exception) {
println("Erreur survenue : ${e.message}")
}
// Erreur survenue : / by zero
println("Le programme continue de s'exécuter !")
}
Gestion d'exceptions spécifiques
fun main() {
try {
val text = "abc"
val number = text.toInt() // NumberFormatException
} catch (e: NumberFormatException) {
println("Impossible de convertir en nombre : ${e.message}")
} catch (e: Exception) {
println("Erreur inconnue : ${e.message}")
}
}
finally
fun main() {
try {
println("Début du traitement")
// Effectuer le traitement
} catch (e: Exception) {
println("Erreur survenue")
} finally {
println("Nettoyage (toujours exécuté)")
}
}
🎯 Exemples pratiques
Conversion sécurisée en nombre
fun safeToInt(text: String): Int? {
return try {
text.toInt()
} catch (e: NumberFormatException) {
null
}
}
fun main() {
println(safeToInt("123")) // 123
println(safeToInt("abc")) // null
val age = safeToInt("25") ?: 0
println("Âge : $age")
}
Simulation de lecture de fichier
class FileReader {
fun readFile(filename: String): String {
if (filename.isEmpty()) {
throw IllegalArgumentException("Le nom de fichier est vide")
}
if (!filename.endsWith(".txt")) {
throw IllegalArgumentException("Seuls les fichiers texte sont pris en charge")
}
return "Contenu du fichier : $filename"
}
}
fun main() {
val reader = FileReader()
try {
val content = reader.readFile("data.txt")
println(content)
} catch (e: IllegalArgumentException) {
println("Erreur : ${e.message}")
}
try {
reader.readFile("")
} catch (e: IllegalArgumentException) {
println("Erreur : ${e.message}")
// Erreur : Le nom de fichier est vide
}
}
Validation des entrées utilisateur
data class User(val name: String, val age: Int)
fun createUser(name: String, age: Int): User {
require(name.isNotBlank()) { "Le nom est obligatoire" }
require(age > 0) { "L'âge doit être positif" }
require(age < 150) { "L'âge est trop élevé" }
return User(name, age)
}
fun main() {
try {
val user1 = createUser("Hong Gildong", 25)
println(user1)
val user2 = createUser("", 25) // Erreur !
} catch (e: IllegalArgumentException) {
println("Échec de validation : ${e.message}")
// Échec de validation : Le nom est obligatoire
}
}
Traitement de réponse API
sealed class Result<out T> {
data class Success<T>(val data: T) : Result<T>()
data class Error(val exception: Exception) : Result<Nothing>()
}
fun divide(a: Int, b: Int): Result<Int> {
return try {
Result.Success(a / b)
} catch (e: ArithmeticException) {
Result.Error(e)
}
}
fun main() {
val result1 = divide(10, 2)
when (result1) {
is Result.Success -> println("Résultat : ${result1.data}")
is Result.Error -> println("Erreur : ${result1.exception.message}")
}
val result2 = divide(10, 0)
when (result2) {
is Result.Success -> println("Résultat : ${result2.data}")
is Result.Error -> println("Erreur : ${result2.exception.message}")
}
}
🔍 Utilisation comme expression
try comme valeur
fun main() {
val number = try {
"123".toInt()
} catch (e: NumberFormatException) {
0 // Valeur par défaut
}
println(number) // 123
val invalid = try {
"abc".toInt()
} catch (e: NumberFormatException) {
-1
}
println(invalid) // -1
}
Chaînage
fun safeDivide(a: Int, b: Int): Int? {
return try {
a / b
} catch (e: ArithmeticException) {
null
}
}
fun main() {
val result = safeDivide(10, 2)
?.let { it * 2 }
?.let { it + 5 }
?: 0
println(result) // 15 (10/2 * 2 + 5)
}
🛡️ Écriture de code sécurisé
require vs check
fun processOrder(quantity: Int, price: Int) {
// Validation des entrées
require(quantity > 0) { "La quantité doit être positive" }
require(price > 0) { "Le prix doit être positif" }
val total = quantity * price
// Validation d'état
check(total < 1000000) { "Le montant total est trop élevé" }
println("Commande terminée : ${total}€")
}
fun main() {
try {
processOrder(10, 5000) // Succès
processOrder(-1, 5000) // Échec require
} catch (e: IllegalArgumentException) {
println("Erreur d'entrée : ${e.message}")
} catch (e: IllegalStateException) {
println("Erreur d'état : ${e.message}")
}
}
checkNotNull
fun processName(name: String?) {
val validName = checkNotNull(name) { "Le nom est null" }
println("Traitement : $validName")
}
fun main() {
try {
processName("Hong Gildong") // Succès
processName(null) // Échec
} catch (e: IllegalStateException) {
println("Erreur : ${e.message}")
}
}
runCatching
Une méthode pratique disponible depuis Kotlin 1.3 !
fun main() {
// Utilisation de runCatching
val result = runCatching {
"123".toInt()
}
result
.onSuccess { println("Succès : $it") }
.onFailure { println("Échec : ${it.message}") }
// getOrNull
val number = runCatching { "abc".toInt() }.getOrNull()
println(number) // null
// getOrDefault
val safe = runCatching { "abc".toInt() }.getOrDefault(0)
println(safe) // 0
}
🎯 Patterns pratiques
Logique de nouvelle tentative
fun <T> retry(times: Int, block: () -> T): T? {
repeat(times) { attempt ->
try {
return block()
} catch (e: Exception) {
println("Tentative ${attempt + 1} échouée : ${e.message}")
if (attempt == times - 1) throw e
}
}
return null
}
fun main() {
var count = 0
try {
val result = retry(3) {
count++
if (count < 3) {
throw Exception("Pas encore prêt")
}
"Succès !"
}
println(result)
} catch (e: Exception) {
println("Échec final")
}
}
Nettoyage des ressources
class Resource : AutoCloseable {
init {
println("Création de ressource")
}
fun use() {
println("Utilisation de ressource")
}
override fun close() {
println("Nettoyage de ressource")
}
}
fun main() {
try {
Resource().use { resource ->
resource.use()
// close() appelé automatiquement
}
} catch (e: Exception) {
println("Erreur survenue")
}
}
🤔 Questions fréquentes
Q1. Quand utiliser throw ?
R: Quand vous voulez lever explicitement une exception !
fun validateAge(age: Int) {
if (age < 0) {
throw IllegalArgumentException("L'âge ne peut pas être négatif")
}
if (age > 150) {
throw IllegalArgumentException("L'âge est trop élevé")
}
}
Q2. Exception vs RuntimeException ?
R: Kotlin ne fait pas de distinction !
// En Java, distinction checked/unchecked
// En Kotlin, toutes sont unchecked (pas besoin de déclaration throws)
fun divide(a: Int, b: Int): Int {
return a / b // Pas besoin de déclaration throws !
}
Q3. Exceptions personnalisées ?
R: Héritez d'Exception !
class InvalidEmailException(message: String) : Exception(message)
fun validateEmail(email: String) {
if (!email.contains("@")) {
throw InvalidEmailException("Email invalide : $email")
}
}
fun main() {
try {
validateEmail("invalid")
} catch (e: InvalidEmailException) {
println(e.message)
}
}
🎬 Conclusion
Créez des programmes stables grâce à la gestion des exceptions !
Points clés :
✅ Gérer les exceptions avec try-catch
✅ Valider avec require/check
✅ Simplifier avec runCatching
✅ Retourner des valeurs comme expression
✅ Nettoyer avec finally
Étape suivante : Apprenez à manipuler les fichiers dans Entrées/Sorties de fichiers !