Bonjour ! Dans la leçon précédente, nous avons appris à prendre le contrôle du flux d'erreurs avec throw et throws. Nous avons même esquissé une première exception personnalisée, UserNotFoundException, pour signaler une erreur métier claire.
Aujourd'hui, nous allons formaliser et approfondir cette notion. L'objectif est de maîtriser la création de classes d'exceptions personnalisées pour représenter des erreurs métier spécifiques. Pourquoi ne pas se contenter de IllegalArgumentException ou RuntimeException ? Car en développement backend professionnel, des exceptions comme SoldeInsuffisantException ou ProduitHorsStockException rendent votre code plus lisible, plus maintenable et vos APIs beaucoup plus expressives. C'est une pratique fondamentale pour construire des systèmes robustes.
À la fin de cette leçon, vous saurez définir vos propres types d'erreurs pour refléter précisément la logique de votre application.
1. Pourquoi créer des exceptions personnalisées ?
Les exceptions intégrées à Java sont puissantes, mais souvent trop génériques. Si votre méthode retirerArgent() lance une IllegalArgumentException, l'appelant sait qu'un argument est invalide, mais il ne sait pas pourquoi. Le montant était-il négatif ? Ou le solde était-il simplement insuffisant ?
Créer vos propres exceptions résout ce problème et apporte plusieurs avantages clés :
- Clarté et Sémantique : Le nom de la classe d'exception lui-même documente l'erreur.
SoldeInsuffisantExceptionest bien plus parlant queException. - Gestion d'erreurs ciblée : Cela vous permet d'écrire des blocs
catchspécifiques pour chaque type d'erreur métier. Vous pouvez ainsi afficher un message spécifique à l'utilisateur pour une erreur de stock, tout en enregistrant une erreur de base de données dans un fichier de logs pour les administrateurs. - Contrat d'API robuste : Lorsque la signature d'une méthode de service inclut
throws ProduitNonTrouveException, elle communique clairement aux autres développeurs une condition d'erreur possible qu'ils doivent gérer.
Pour approfondir cette idée, l'article suivant illustre parfaitement l'intérêt de créer des exceptions qui correspondent à la logique de votre application.
Gestion des Exceptions en Java : Guide Complet - DEV Community
Cet article de dev.to montre des exemples très concrets d'exceptions personnalisées pour des règles métier.
Lisez la section "5. Exceptions Personnalisées". Portez une attention particulière aux deux exemples : SoldeInsuffisantException pour une application bancaire. AgeInvalideException pour la validation d'un utilisateur. Observez comment, dans les deux cas, l'exception personnalisée permet de représenter une règle métier qui a été violée.
2. Comment créer une classe d'exception personnalisée
Créer une exception personnalisée en Java est étonnamment simple : il s'agit de créer une classe qui hérite d'une classe d'exception existante.
La structure de base est la suivante :
public class NomDeMonException extends Exception {
// ... constructeurs ...
}
La décision la plus importante est de choisir de quelle classe hériter :
extends Exception: Crée une exception contrôlée (checked). Le compilateur forcera l'appelant à la gérer (avectry-catchouthrows). C'est le choix par défaut pour les erreurs métier prévisibles et récupérables (ex: "le panier est vide", "le coupon a expiré").extends RuntimeException: Crée une exception non contrôlée (unchecked). L'appelant n'est pas obligé de la gérer. On l'utilise pour des erreurs de programmation ou des états anormaux qui ne devraient pas se produire dans un système fonctionnel (ex: une configuration essentielle est manquante).
Les constructeurs : le cœur de votre exception
Une fois la classe créée, l'étape la plus importante est de définir ses constructeurs. Ces derniers permettent de transmettre des informations utiles sur l'erreur. Ils appellent presque toujours le constructeur de la classe parente via le mot-clé super().
La vidéo suivante vous guidera pas à pas dans la création d'une exception personnalisée.
JAVA (Intermédiaire) - 75 - Créer sa propre exception en Java
La vidéo "JAVA (Intermédiaire) - 75 - Créer sa propre exception en Java" de la chaîne LES TEACHERS DU NET est un excellent tutoriel pratique.
Suivez les étapes présentées par l'instructeur : Bonne pratique : Notez l'idée d'organiser les exceptions dans un package dédié (ex: com.monprojet.exceptions). Création de la classe : Voyez comment la classe AgeInvalidException est créée et comment elle hérite de Exception. Ajout du constructeur : C'est la partie la plus importante. Observez comment un constructeur qui prend un String message est ajouté, et comment super(message) est utilisé pour passer ce message à la classe Exception parente. Lancement de l'exception : Enfin, voyez comment throw new AgeInvalidException(...) remplace une exception plus générique, rendant le code plus spécifique.
L'image ci-dessous résume la structure d'une classe d'exception personnalisée, montrant plusieurs constructeurs courants que vous pouvez inclure.

3. Bonnes pratiques
Lorsque vous concevez vos exceptions, gardez ces quelques règles en tête pour assurer la qualité de votre code.
Maîtriser la gestion des exceptions en Java : guide essentiel - Anaska
Cet article d'Anaska résume les bonnes pratiques de conception pour les exceptions personnalisées.
Lisez la sous-section "Bonnes pratiques pour exceptions personnalisées". Elle contient des recommandations clés.
Voici un résumé des points essentiels :
- Nommez-les clairement : Le nom doit être descriptif et se terminer par
Exception(ex:PaymentGatewayTimeoutException). - Fournissez des messages utiles : Le message d'erreur doit permettre à un développeur de comprendre le contexte de l'erreur sans avoir à lire tout le code source. Incluez des valeurs pertinentes (ex:
"L'utilisateur avec l'ID '123' n'a pas été trouvé."). - N'utilisez pas les exceptions pour le flux de contrôle : Les exceptions doivent signaler des conditions exceptionnelles, pas être utilisées pour gérer la logique normale de l'application (comme une boucle
for). Elles ont un coût en performance. - Préservez la hiérarchie : Si vous avez plusieurs exceptions liées, vous pouvez les faire hériter d'une exception de base commune. Par exemple,
InvalidCardExceptionetExpiredCardExceptionpourraient toutes deux hériter d'une exception de basePaymentException.
4. Exercice : Gestion de stock
Mettons en pratique ces concepts avec un scénario d'e-commerce. Vous allez créer un service qui gère l'achat de produits et qui doit lancer une exception si le stock est insuffisant.
Votre mission :
-
Créer l'exception personnalisée :
Créez une nouvelle classeInsufficientStockExceptionqui hérite deException(ce sera donc une exception contrôlée).
Elle doit avoir un constructeur qui accepte unStringpour le message d'erreur.// Fichier InsufficientStockException.java public class InsufficientStockException extends Exception { public InsufficientStockException(String message) { super(message); } } -
Créer la couche de service :
Créez une classeProductService. À l'intérieur, simulez un stock avec unHashMap.
Créez une méthodepurchaseProduct(String productId, int quantity)qui :- Déclare qu'elle peut lancer
InsufficientStockException. - Vérifie si la quantité demandée est supérieure au stock disponible.
- Si c'est le cas, elle doit lancer
new InsufficientStockException(...)avec un message clair. - Sinon, elle met à jour le stock et affiche un message de succès.
- Déclare qu'elle peut lancer
-
Appeler le service :
Dans votre méthodemain, créez une instance deProductServiceet appelez la méthodepurchaseProductdans un bloctry-catchpour gérer à la fois le cas de succès et le cas d'échec.
Cliquez ici pour voir la solution
import java.util.HashMap;
import java.util.Map;
// Fichier ProductService.java
class ProductService {
private Map<String, Integer> stock = new HashMap<>();
public ProductService() {
// Initialisation du stock pour la simulation
stock.put("PROD-001", 10);
stock.put("PROD-002", 5);
}
public void purchaseProduct(String productId, int quantity) throws InsufficientStockException {
Integer availableStock = stock.get(productId);
if (availableStock == null) {
// On pourrait créer une autre exception comme ProductNotFoundException
throw new IllegalArgumentException("Produit avec l'ID " + productId + " n'existe pas.");
}
if (quantity > availableStock) {
String errorMessage = "Stock insuffisant pour le produit " + productId
+ ". Demandé : " + quantity + ", Disponible : " + availableStock;
throw new InsufficientStockException(errorMessage);
}
// Si le stock est suffisant, on met à jour
stock.put(productId, availableStock - quantity);
System.out.println("Achat réussi ! " + quantity + " unité(s) de " + productId + ". Nouveau stock : " + stock.get(productId));
}
}
// Fichier Main.java
public class Main {
public static void main(String[] args) {
ProductService productService = new ProductService();
// Cas 1 : Tentative d'achat avec stock insuffisant
System.out.println("--- Tentative d'achat de 15 unités de PROD-001 ---");
try {
productService.purchaseProduct("PROD-001", 15);
} catch (InsufficientStockException e) {
System.err.println("Erreur métier capturée : " + e.getMessage());
}
System.out.println("\n--- Tentative d'achat avec stock suffisant ---");
// Cas 2 : Achat réussi
try {
productService.purchaseProduct("PROD-002", 3);
} catch (InsufficientStockException e) {
System.err.println("Cette erreur ne devrait pas se produire : " + e.getMessage());
}
}
}
Résultat attendu :
--- Tentative d'achat de 15 unités de PROD-001 ---
Erreur métier capturée : Stock insuffisant pour le produit PROD-001. Demandé : 15, Disponible : 10
--- Tentative d'achat avec stock suffisant ---
Achat réussi ! 3 unité(s) de PROD-002. Nouveau stock : 2
Conclusion
Félicitations ! Vous savez maintenant comment enrichir votre gestion d'erreurs en créant des exceptions qui ont un sens métier. C'est une compétence qui distingue un code amateur d'un code professionnel, car elle améliore radicalement la maintenabilité et la clarté de vos applications.
Les points clés à retenir :
- Les exceptions personnalisées permettent de représenter des erreurs métier spécifiques, rendant le code plus sémantique.
- On les crée en définissant une classe qui hérite de
Exception(checked) ouRuntimeException(unchecked). - Les constructeurs sont essentiels pour transmettre un message d'erreur clair et la cause originelle via
super(). - L'utilisation d'exceptions personnalisées permet une gestion d'erreurs ciblée avec des blocs
catchspécifiques.
Maintenant que nous savons créer et gérer des erreurs de manière robuste, comment pouvons-nous nous assurer que notre code se comporte comme prévu, y compris lorsque ces erreurs se produisent ? Dans le prochain module, nous aborderons les tests unitaires avec JUnit. Vous apprendrez à écrire des tests automatisés qui non seulement valident le fonctionnement normal de votre code, mais vérifient aussi que vos exceptions personnalisées sont bien lancées au bon moment.