Java : try / catch / finally et return
Voici un code simple en Java :
package com.gradot.blog;
public class ExceptionAndReturn {
public static final int NORMAL = 0;
public static final int EXCEPTION = 42;
public static final int FINALLY = 66;
public static void main(String[] args) {
System.out.println("foo returned " + foo());
}
public static int foo() {
try {
System.out.println("Try");
Object o = null;
o.getClass();
return NORMAL;
} catch (NullPointerException e) {
System.out.println("Catch");
return EXCEPTION;
} finally {
System.out.println("Finally");
return FINALLY;
}
}
}
De manière tout à fait évidente, l’appel à o.getClass(); va provoquer une exception et on va entrer dans le bloc catch. Que va alors afficher ce code ? Quelle valeur est renvoyée ? Exécutons et regardons :
Try Catch Finally foo returned 66
Le passage dans les 3 blocs de gestion d’exception est attendu. Le retour de la valeur 66 est peut-être moins évident mais en même temps c’est logique : on termine par un return FINALLY;.
Modifions légèrement la méthode foo() pour que ne survienne pas l’exception :
public static int foo() {
try {
System.out.println("Try");
Object o = new Object();
o.getClass();
return NORMAL;
} catch (NullPointerException e) {
System.out.println("Catch");
return EXCEPTION;
} finally {
System.out.println("Finally");
return FINALLY;
}
}
On ne rentre plus dans le bloc catch mais on a bien sûr le même problème : la valeur retournée dans le bloc finally écrase celle donnée par un éventuel précédent return. On obtient en console :
Try
Finally
foo returned 66
Revenons à une version du code provoquant une exception mais essayons de la relancer dans le catch plutôt que de retourner un code d’erreur :
public static int foo() {
try {
System.out.println("Try");
Object o = null;
o.getClass();
return NORMAL;
} catch (NullPointerException e) {
System.out.println("Catch");
throw e;
} finally {
System.out.println("Finally");
return FINALLY;
}
}
Quel affichage d’après vous ? Et bien, de manière un peu surprenante (bien que vous deviez sentir le piège), le return du bloc finally annule et remplace le throw du bloc catch. En console :
Try Catch Finally foo returned 66
Tentons carrément de supprimer le bloc catch :
public static int foo() {
try {
System.out.println("Try");
Object o = null;
o.getClass();
return NORMAL;
} finally {
System.out.println("Finally");
return FINALLY;
}
}
catch ou pas, le résultat est le même, le finally est exécuté et l’exception n’est pas remontée à l’appelant.
La conclusion est toute simple : ne mettez pas de return dans un finally. Vous masqueriez les précédents returns et vous bloqueriez la levée d’exception ! Vous avez eu chaud ? Pas tant que ça : si vous mettez les codes ci-dessus dans Eclipse (ou si vous le compiler manuellement), vous verrez un warning sur l’ensemble du bloc finally :
finally block does not complete normally


