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

Publicités

Laisser un commentaire

Entrez vos coordonnées ci-dessous ou cliquez sur une icône pour vous connecter:

Logo WordPress.com

Vous commentez à l'aide de votre compte WordPress.com. Déconnexion / Changer )

Image Twitter

Vous commentez à l'aide de votre compte Twitter. Déconnexion / Changer )

Photo Facebook

Vous commentez à l'aide de votre compte Facebook. Déconnexion / Changer )

Photo Google+

Vous commentez à l'aide de votre compte Google+. Déconnexion / Changer )

Connexion à %s