Java Vs C# : La gestion des exceptions


Warning, warning, ceci n’est pas une rubrique à troll !!

Bien qu’étant plutot spécialiste Java, je me suis mis récemment au C#. Loin des débats qui n’en finissent plus pour savoir lequel des deux p… le plus loin, je tenais à faire une petite rubrique pour lister « les trucs qui tuent » qui existent dans chaque langage et que tout bon développeur aime connaitre.

Et donc le truc qui tue du jour :

La gestion des exceptions en C# et Java

A première vue, rien qui semble différencier la gestion des exceptions en C# et Java. La syntaxe est quasi identique :

try
{
 ...
}
catch (NullPointerException e)
{
 ...
 throw e;
}
 finally
 {
 ...
 }

et

try
{
 ...
}
 catch (NullReferenceException)
{
 ...
 throw ;
}
finally
{
 ...
}

Non seulement la syntaxe est très proche mais les Exceptions forment une hiérarchie héritant à chaque fois d’une classe mère dans chacun des deux langages.

Cependant cette hiérarchie connait une différence importante en Java :

  • Chaque Exception dérive de Throwable
  • Throwable a 2 types dérivées : Error et Exception
  • Les exceptions sont partagées en « checked exception » et « unchecked exception« 

Autrement dit, il existe des exceptions qu’il ne faut pas attraper (les Error : OutOfMemoryError, NoClassDeFoundError etc…), des exceptions qu’on n’est pas obligé d’attraper (les unchecked) et des exceptions qu’il faut obligatoirement traiter si elles sont lancées (les checked).

Et pour préciser les exceptions qu’un code peut lancer, une méthode doit déclarer ces exceptions dans sa signature :

 

public void method() throws Exception1, Exception1
{
 // ...
}

Cette différence très importante va permettre au compilateur de vérifier la cohérence du code. L’appel d’une méthode entrainera obligatoirement le traitement des exceptions déclarées en signature.

Ainsi les codes suivants seront valides :

private void anotherMethod()
{
    try
    {
       method();
    }
    catch (Exception1 e1)
    {
    }
    catch (Exception2 e2)
    {
    }
}

// si on ne les attrape pas, il faut au moins les relancer et le déclarer
private void anotherMethod() throws Exception1, Exception1
{
    method();
}

Par contre le code suivant ne compilera pas :

private void anotherMethod()
{
 try
 {
     method();
 }
 catch (Exception1 e1)
 {
 }   
 // Erreur : on n'attrape pas l'exception Exception2
}
 

private void anotherMethod()
{
 try
 {
 method();
 }
 catch (Exception1 e1)
 {
 }   
 catch (Exception2 e2)
 {
 }
 // Erreur : cette checked exception n'est pas lancé et ce bloc de code est donc innateignable
 catch (Exception3 e3)
 {
 }
}

Autre truc qui tue, en Java7 la syntaxe va évoluer pour permettre le multi catch des exceptions, très pratique quand on réalise les mêmes traitements pour chaque exception attrapée :

catch (Exception1|Exception2 ex)
{
 logger.log(ex);
 throw ex;
}

10 réflexions sur “Java Vs C# : La gestion des exceptions

    • Et oui, effectivement c’est comme tout, il faut savoir quand utiliser des checked ou des unchecked et quand traiter une erreur ^^
      Pour avoir récemment bossé sur du C# c’est quand même bien confortable en Java quand le compilateur prévient qu’une exception n’est pas lancé (donc que le bloc try catch est inutile) ou au contraire qu’un bloc est susceptible de lancer quelque chose.
      En C#, sur l’application que j’ai vu, et je ne parlerais donc que de celle-ci, tout les catch sont fait sur Exception puisque les programmeurs n’ont aucune idée de ce qui peut être lancé en dessous. Du coup ils attrapent tout, au cas ou.
      Merci pour les liens en tout cas, je lis de ce pas

    • Et oui, effectivement c’est comme tout, il faut savoir quand utiliser des checked ou des unchecked et quand traiter une erreur ^^
      Pour avoir récemment bossé sur du C# c’est quand même bien confortable en Java quand le compilateur prévient qu’une exception n’est pas lancé (donc que le bloc try catch est inutile) ou au contraire qu’un bloc est susceptible de lancer quelque chose.
      En C#, sur l’application que j’ai vu, et je ne parlerais donc que de celle-ci, tout les catch sont fait sur Exception puisque les programmeurs n’ont aucune idée de ce qui peut être lancé en dessous. Du coup ils attrapent tout, au cas ou.
      Merci pour les liens en tout cas, je lis de ce pas

  1. J’ai du mal a comprendre quand Java te prévient qu’une expression n’est pas lancée… vu que n’importe quel bout de code, même quand il ne déclare aucune exception, est capable d’en thrower une…

    Tu veux peut-etre parler du cas ou tu catches une checked exception qui n’est throwée a aucun endroit de ton try. En effet dans ce cas de figure ca n’a pas de sens vu qu’on est sur et certain que cela n’arrivera jamais puisqu’aucune methode n’en déclare:) meme si une methode dans le try est overridée, vu que c’est impossible de déclarer dans une methode fille une checked exception qui n’est pas déjà dans la methode mère.

    Toujours est-il que sur ce même bout de code, une unchecked exception est toujours succeptible d’être balancée, alors au final les C#eur n’ont peut-être pas tort de la catcher… après a voir comment ils gèrent l’exception… 🙂

  2. J’ai du mal a comprendre quand Java te prévient qu’une expression n’est pas lancée… vu que n’importe quel bout de code, même quand il ne déclare aucune exception, est capable d’en thrower une…

    Tu veux peut-etre parler du cas ou tu catches une checked exception qui n’est throwée a aucun endroit de ton try. En effet dans ce cas de figure ca n’a pas de sens vu qu’on est sur et certain que cela n’arrivera jamais puisqu’aucune methode n’en déclare:) meme si une methode dans le try est overridée, vu que c’est impossible de déclarer dans une methode fille une checked exception qui n’est pas déjà dans la methode mère.

    Toujours est-il que sur ce même bout de code, une unchecked exception est toujours succeptible d’être balancée, alors au final les C#eur n’ont peut-être pas tort de la catcher… après a voir comment ils gèrent l’exception… 🙂

  3. Oui c’est bien le point que tu cites. Je met de côté volontairement les unchecked exception qui sont par nature des erreurs dans le code dont en théorie on ne peut pas traiter la cause.
    Pour les checked, elles représentent un contrat et un cas d’erreur de fonctionnement usuel.
    Et ca m’intéresse de savoir quand mon contrat évolue pour faire évoluer mon traitement des erreurs.

    Pour avoir lu les liens que tu donnais plus haut, oui je comprends bien le point cité par les différents auteurs. Cependant j’ai l’impression de toujours revenir au débat classique entre « donner de la puissance au langage au risque d’une mauvaise utilisation » ou « contraindre le langage pour ne pas permettre d’erreur » (Cf les débats sur l’héritage multiple, la surcharge des opérateurs, les extensions de méthode, etc…).

  4. Juste pour compléter histoire d’illustrer ce que je voulais dire quand je parlais de faire la différence entre erreurs « attendues » et erreurs exceptionnelles.
    Quand par exemple j’ouvre une connexion réseau et qu’une erreur usuelle dans ce cas de figure c’est l’impossibilité d’arriver à créer cette connexion, je peux avoir une politique de retry.
    Par contre pour une nullpointerexception j’ai pas de politique particulière.
    Traiter ces deux erreurs au même niveau par un catch Exception je ne trouve donc pas cela juste, on confond les erreurs « attendues » et les erreurs de code.
    Evidemment si je suis « fort » je peux savoir tout seul qu’il faut faire le bon catch qui va bien parce que je connais mon métier et qu’une politique de retry c’est habituel sur l’établissement d’une connexion. Il y a pas mal de cas ou ca demanderait une vigilance de tout les instants. Personnellement je préfère me reposer sur le contrat donné par la signature de la méthode qui me liste l’exhaustivité des erreurs « attendues ».
    Donc non, pour moi le C#er que je citais et qui faisait l’équivalent d’un catch Throwable dans chaque méthode n’avait pas la bonne facon de faire.
    (Selon moi, donc pour ce que ca vaut ^^)

  5. Oui c’est bien le point que tu cites. Je met de côté volontairement les unchecked exception qui sont par nature des erreurs dans le code dont en théorie on ne peut pas traiter la cause.
    Pour les checked, elles représentent un contrat et un cas d’erreur de fonctionnement usuel.
    Et ca m’intéresse de savoir quand mon contrat évolue pour faire évoluer mon traitement des erreurs.

    Pour avoir lu les liens que tu donnais plus haut, oui je comprends bien le point cité par les différents auteurs. Cependant j’ai l’impression de toujours revenir au débat classique entre « donner de la puissance au langage au risque d’une mauvaise utilisation » ou « contraindre le langage pour ne pas permettre d’erreur » (Cf les débats sur l’héritage multiple, la surcharge des opérateurs, les extensions de méthode, etc…).

  6. Juste pour compléter histoire d’illustrer ce que je voulais dire quand je parlais de faire la différence entre erreurs « attendues » et erreurs exceptionnelles.
    Quand par exemple j’ouvre une connexion réseau et qu’une erreur usuelle dans ce cas de figure c’est l’impossibilité d’arriver à créer cette connexion, je peux avoir une politique de retry.
    Par contre pour une nullpointerexception j’ai pas de politique particulière.
    Traiter ces deux erreurs au même niveau par un catch Exception je ne trouve donc pas cela juste, on confond les erreurs « attendues » et les erreurs de code.
    Evidemment si je suis « fort » je peux savoir tout seul qu’il faut faire le bon catch qui va bien parce que je connais mon métier et qu’une politique de retry c’est habituel sur l’établissement d’une connexion. Il y a pas mal de cas ou ca demanderait une vigilance de tout les instants. Personnellement je préfère me reposer sur le contrat donné par la signature de la méthode qui me liste l’exhaustivité des erreurs « attendues ».
    Donc non, pour moi le C#er que je citais et qui faisait l’équivalent d’un catch Throwable dans chaque méthode n’avait pas la bonne facon de faire.
    (Selon moi, donc pour ce que ca vaut ^^)

Laisser un commentaire