web-dev-qa-db-fra.com

Comment encapsuler les exceptions vérifiées mais conserver les exceptions d'exécution d'origine dans Java

J'ai du code qui peut lever des exceptions vérifiées et d'exécution.

Je voudrais intercepter l'exception vérifiée et l'encapsuler avec une exception d'exécution. Mais si une RuntimeException est levée, je n'ai pas besoin de l'envelopper car c'est déjà une exception d'exécution.

La solution que j'ai a un peu de frais généraux et n'est pas "soignée":

try {
  // some code that can throw both checked and runtime exception
} catch (RuntimeException e) {
  throw e;
} catch (Exception e) {
  throw new RuntimeException(e);
}

ne idée d'une manière plus élégante?

27
AlikElzin-kilaka

J'utilise une relance "aveugle" pour passer les exceptions vérifiées. Je l'ai utilisé pour passer par l'API Streams où je ne peux pas utiliser de lambdas qui lèvent des exceptions vérifiées. Par exemple, nous avons des interfaces fonctionnelles ThrowingXxxxx afin que l'exception vérifiée puisse être transmise.

Cela me permet d'attraper naturellement l'exception vérifiée dans un appelant sans avoir besoin de savoir qu'un appelé devait la passer par une interface qui n'autorisait pas les exceptions vérifiées.

try {
  // some code that can throw both checked and runtime exception

} catch (Exception e) {
  throw rethrow(e);
}

Dans une méthode d'appel, je peux déclarer à nouveau l'exception vérifiée.

public void loadFile(String file) throws IOException {
   // call method with rethrow
}

/**
 * Cast a CheckedException as an unchecked one.
 *
 * @param throwable to cast
 * @param <T>       the type of the Throwable
 * @return this method will never return a Throwable instance, it will just throw it.
 * @throws T the throwable as an unchecked throwable
 */
@SuppressWarnings("unchecked")
public static <T extends Throwable> RuntimeException rethrow(Throwable throwable) throws T {
    throw (T) throwable; // rely on vacuous cast
}

Il existe de nombreuses options différentes pour gérer les exceptions. Nous en utilisons quelques-uns.

https://Vanilla-Java.github.io/2016/06/21/Reviewing-Exception-Handling.html

28
Peter Lawrey

Throwables.propagate() de la goyave fait exactement ceci:

try {
    // some code that can throw both checked and runtime exception
} catch (Exception e) {
    throw Throwables.propagate(e);
}

MISE À JOUR: Cette méthode est désormais obsolète. Voir cette page pour une explication détaillée.

18
shmosel

Pas vraiment.

Si vous faites beaucoup cela, vous pouvez le ranger dans une méthode d'assistance.

static RuntimeException unchecked(Throwable t){
    if (t instanceof RuntimeException){
      return (RuntimeException) t;
    } else if (t instanceof Error) { // if you don't want to wrap those
      throw (Error) t;
    } else {
      return new RuntimeException(t);
    }
}

try{
 // ..
}
catch (Exception e){
   throw unchecked(e);
}
5
Thilo

J'ai un fichier .class spécialement compilé contenant les éléments suivants:

public class Thrower {
    public static void Throw(Java.lang.Throwable t) {
        throw t;
    }
}

Ça marche. Le compilateur Java refuserait normalement de le compiler, mais le vérificateur de bytecode ne s'en soucie pas du tout.

Le cours est utilisé de manière similaire à la réponse de Peter Lawrey:

try {
  // some code that can throw both checked and runtime exception

} catch (Exception e) {
    Thrower.Throw(e);
}
2
Joshua

J'utilise généralement le même type de structure de code, mais le condenser en une seule ligne dans l'une des rares fois où un opérateur ternaire améliore réellement le code:

try {
  // code that can throw
}
catch (Exception e) {
  throw (e instanceof RuntimeException) ? (RuntimeException) e : new RuntimeException(e);
}

Cela ne nécessite pas de méthodes supplémentaires ni de blocs catch c'est pourquoi j'aime ça.

1
user439793

Vous pouvez réécrire la même chose en utilisant l'opérateur instanceof

try {
    // some code that can throw both checked and runtime exception
} catch (Exception e) {
    if (e instanceof RuntimeException) {
        throw e;
    } else {
        throw new RuntimeException(e);
    }
}

Cependant, votre solution semble meilleure.

1
Tionio

Le problème est que Exception est trop large. Vous devez savoir exactement quelles sont les exceptions vérifiées possibles.

try {
    // code that throws checked and unchecked exceptions
} catch (IOException | SomeOtherException ex) {
    throw new RuntimeException(ex);
}

Les raisons pour lesquelles cela ne fonctionnerait pas révèlent des problèmes plus profonds qui devraient être résolus à la place:

Si une méthode déclare qu'elle throws Exception alors c'est trop large. Savoir que "quelque chose peut mal tourner" sans autre information n'est d'aucune utilité pour un appelant. La méthode doit utiliser des classes d'exceptions spécifiques dans une hiérarchie significative, ou utiliser des exceptions non vérifiées, le cas échéant.

Si une méthode lève trop de types d'exceptions vérifiées, c'est trop compliqué. Il doit être soit refactorisé en plusieurs méthodes plus simples, soit les exceptions doivent être organisées dans une hiérarchie d'héritage sensible, selon la situation.

Bien sûr, il peut y avoir des exceptions à la règle. Déclarer une méthode throws Exception peut être parfaitement raisonnable s'il est utilisé par une sorte de framework transversal (comme JUnit ou AspectJ ou Spring) plutôt que de comprendre une API pour les autres.

1
OrangeDog