web-dev-qa-db-fra.com

Existe-t-il un moyen de faire en sorte que run () runnable lève une exception?

Une méthode que j'appelle dans run () dans une classe implémentant Runnable ) est conçue pour lancer une exception.

Mais le compilateur Java ne me laisse pas faire cela et suggère de l’entourer de try/catch.

Le problème est qu’en l’entourant d’un try/catch, je fais thatrun () inutile. Je do veux lancer cette exception.

Si je spécifie throws pour run () même, le compilateur se plaint que Exception is not compatible with throws clause in Runnable.run().

D'habitude, je suis totalement d'accord avec le fait de ne pas laisser run () jeter une exception. Mais j'ai une situation unique dans laquelle je dois avoir cette fonctionnalité.

Comment puis-je contourner cette limitation?

59
Regex Rookie

Si vous voulez passer une classe qui implémente Runnable dans le framework Thread, vous devez alors respecter les règles de ce framework. Voir la réponse de Ernest Friedman-Hill pour expliquer pourquoi le faire autrement est une mauvaise idée.

Cependant, je suis persuadé que vous souhaitez appeler la méthode run directement dans votre code afin que votre code d'appel puisse traiter l'exception.

La réponse à ce problème est facile. N'utilisez pas l'interface Runnable de la bibliothèque Thread, mais créez votre propre interface avec la signature modifiée qui permet de générer une exception vérifiée, par exemple.

public interface MyRunnable
{
    void myRun ( ) throws MyException;
}

Vous pouvez même créer un adaptateur qui convertit cette interface en une vraie Runnable (en traitant les exceptions vérifiées) appropriée pour une utilisation dans la structure Thread.

16

Vous pouvez utiliser une Callable à la place, en la soumettant à une ExecutorService et en attendant le résultat avec FutureTask.isDone() renvoyé par la ExecutorService.submit()

Lorsque isDone() renvoie true, vous appelez FutureTask.get(). Maintenant, si votre Callable a lancé une Exception, alors FutureTask.get() lançera aussi une Exception et l’exception originale à laquelle vous pourrez accéder en utilisant Exception.getCause()

66

Si run() lançait une exception vérifiée, qu'est-ce qui l'attraperait? Vous ne pouvez pas inclure cet appel run() dans un gestionnaire, car vous n'écrivez pas le code qui l'invoque.

Vous pouvez intercepter votre exception vérifiée dans la méthode run() et lancer une exception non redirigée (c'est-à-dire, RuntimeException) à sa place. Cela mettra fin au thread avec une trace de pile; c'est peut-être ce que vous recherchez.

Si vous souhaitez plutôt que votre méthode run() rapporte l'erreur quelque part, vous pouvez simplement fournir une méthode de rappel pour le bloc catch de la méthode run() à appeler; cette méthode peut stocker l'objet exception quelque part, puis votre thread intéressé peut trouver l'objet à cet emplacement. 

18

Oui, il existe un moyen de lancer une exception vérifié de la méthode run(), mais c'est tellement terrible que je ne la partagerai pas. 

Voici ce que vous pouvez faire à la place. il utilise le même mécanisme que celui utilisé par une exception d'exécution:

@Override
public void run() {
  try {
    /* Do your thing. */
    ...
  } catch (Exception ex) {
    Thread t = Thread.currentThread();
    t.getUncaughtExceptionHandler().uncaughtException(t, ex);
  }
}

Comme d'autres l'ont noté, si votre méthode run() est vraiment la cible d'une Thread, il est inutile de lever une exception car elle est non observable. le fait de lancer une exception a le même effet que de ne pas lancer une exception (aucune).

Si ce n'est pas une cible Thread, n'utilisez pas Runnable. Par exemple, peut-être Callable est un meilleur ajustement.

15
erickson
@FunctionalInterface
public interface CheckedRunnable<E extends Exception> extends Runnable {

    @Override
    default void run() throws RuntimeException {
        try {
            runThrows();
        }
        catch (Exception ex) {
            throw new RuntimeException(ex);
        }
    }

    void runThrows() throws E;

}
2
Sina Madani

Je pense qu'un modèle d'auditeur pourrait vous aider avec ce scénario. En cas d'exception dans votre méthode run(), utilisez un bloc try-catch et envoyez-y une notification d'événement d'exception. Et puis gérer votre événement de notification. Je pense que ce serait une approche plus propre. Ce lien SO vous donne un pointeur utile sur cette direction.

0
Sujay

Certaines personnes essaient de vous convaincre que vous devez respecter les règles. Écoutez, mais si vous obéissez, vous devriez décider vous-même en fonction de votre situation. La réalité est "vous DEVEZ respecter les règles" (et non "vous DEVEZ respecter les règles"). Sachez simplement que si vous ne respectez pas les règles, cela pourrait avoir des conséquences.

La situation s'applique non seulement à la situation de Runnable, mais à Java 8 également très fréquemment dans le contexte de Streams et d'autres emplacements où des interfaces fonctionnelles ont été introduites sans possibilité de traiter les exceptions vérifiées. Par exemple, Consumer, Supplier, Function, BiFunction et ainsi de suite ont tous été déclarés sans la possibilité de traiter les exceptions vérifiées.

Quelles sont donc les situations et les options? Dans le texte ci-dessous, Runnable est représentatif de toute interface fonctionnelle ne déclarant pas d'exceptions ou déclarant des exceptions trop limitées pour le cas d'utilisation considéré.

  1. Vous avez déclaré Runnable quelque part vous-même et pouvez remplacer Runnable par quelque chose d'autre .
    1. Envisagez de remplacer Runnable par Callable<Void>. Fondamentalement la même chose, mais autorisé à jeter des exceptions; et doit finalement return null, ce qui est un léger désagrément.
    2. Pensez à remplacer Runnable par votre propre @FunctionalInterface personnalisé, capable d'exécuter exactement les exceptions souhaitées.
  2. Vous avez utilisé une API et des alternatives sont disponibles. Par exemple, certaines API Java sont surchargées, vous pouvez donc utiliser Callable<Void> au lieu de Runnable.
  3. Vous avez utilisé une API et il n'y a pas d'alternative. Dans ce cas, vous n’êtes toujours pas à court d’options .
    1. Vous pouvez encapsuler l'exception dans RuntimeException.
    2. Vous pouvez pirater l'exception dans une exception RuntimeException en utilisant une distribution non contrôlée.

Vous pouvez essayer ce qui suit. C'est un peu un bidouillage, mais parfois un bidouillage est ce dont nous avons besoin. En effet, si une exception doit être cochée ou non cochée, elle est définie par son type, mais doit en pratique être définie par la situation.

@FunctionalInterface
public interface ThrowingRunnable extends Runnable {
    @Override
    default void run() {
        try {
            tryRun();
        } catch (final Throwable t) {
            throwUnchecekd(t);
        }
    }

    private static <E extends RuntimeException> void throwUnchecked(Throwable t) {
        throw (E) t;
    }

    void tryRun() throws Throwable;
}

Je préfère ceci à new RuntimeException(t) car sa trace de pile est plus courte.

Vous pouvez maintenant faire:

executorService.submit((ThrowingRunnable) () -> {throw new Exception()});

Clause de non-responsabilité: la possibilité d'effectuer des conversions non vérifiées de cette manière pourrait en fait être supprimée dans les futures versions de Java, lorsque les informations de type génériques sont traitées non seulement à la compilation, mais également à l'exécution.

0
Christian Hujer