“Sneaky Throws” en Java

"Sournois Throws" en Java

1. Vue d'ensemble

En Java, le concept sneaky throw nous permet de lever toute exception vérifiée sans la définir explicitement dans la signature de la méthode. Cela permet l'omission de la déclarationthrows, imitant effectivement les caractéristiques d'une exception d'exécution.

Dans cet article, nous allons voir comment cela est réalisé en pratique, en examinant quelques exemples de code.

2. À propos des lancers sournois

Checked exceptions are part of Java, not the JVM. Dans le bytecode, nous pouvons lever n'importe quelle exception de n'importe où, sans restrictions.

Java 8 a apporté une nouvelle règle d'inférence de type qui stipule qu'unthrows T est déduit commeRuntimeException chaque fois que cela est autorisé. Cela donne la possibilité d'implémenter des lancers sournois sans la méthode d'assistance.

Un problème avecsneaky throws est que vous souhaiterez probablement intercepter les exceptions éventuellement, mais le compilateur Java ne vous permet pas d’attraper les exceptions vérifiées lancées sournoisement en utilisant le gestionnaire d’exceptions pour leur type d’exception particulier.

3. Les lancers sournois en action

Comme nous l'avons déjà mentionné, le compilateur et le Jave Runtime peuvent voir différentes choses:

public static  void sneakyThrow(Throwable e) throws E {
    throw (E) e;
}

private static void throwsSneakyIOException() {
    sneakyThrow(new IOException("sneaky"));
}

The compiler sees the signature with the throws T inferred to a RuntimeException type, il permet donc à l'exception non cochée de se propager. Java Runtime ne voit aucun type dans les lancers car tous les lancers sont identiques à un simplethrow e.

Ce test rapide illustre le scénario:

@Test
public void whenCallSneakyMethod_thenThrowSneakyException() {
    try {
        SneakyThrows.throwsSneakyIOException();
    } catch (Exception ex) {
        assertEquals("sneaky", ex.getMessage().toString());
    }
}

Il est possible de lever une exception vérifiée en utilisant la manipulation de bytecode, ouThread.stop(Throwable), mais c'est compliqué et déconseillé.

4. Utilisation des annotations Lombok

L'annotation@SneakyThrows deLombok vous permet de lever des exceptions vérifiées sans utiliser la déclarationthrows. Ceci est pratique lorsque vous devez lever une exception à partir d'une méthode dans des interfaces très restrictives commeRunnable.

Disons que nous lançons une exception depuis unRunnable; il ne sera transmis qu'au gestionnaire d'exceptions non gérées deThread’s.

Ce code lancera l'instance deException, il n'est donc pas nécessaire que vous l'enveloppiez dans unRuntimeException:

public class SneakyRunnable implements Runnable {
    @SneakyThrows(InterruptedException.class)
    public void run() {
        throw new InterruptedException();
    }
}

Un inconvénient de ce code est que vous ne pouvez pas intercepter une exception vérifiée qui n'est pas déclarée; so, it will not compile.

Voici la forme correcte pour lancer une exception sournoise:

@SneakyThrows
public void run() {
    try {
        throw new InterruptedException();
    } catch (InterruptedException e) {
        e.printStackTrace();
    }
}

Et voici le test de ce comportement:

@Test
public void whenCallSneakyRunnableMethod_thenThrowException() {
    try {
        new SneakyRunnable().run();
    } catch (Exception e) {
        assertEquals(InterruptedException.class, e.getStackTrace());
    }
}

5. Conclusion

Comme nous l'avons vu dans cet article, le compilateur Java peut être trompé pour traiter les exceptions vérifiées comme non contrôlées.

Comme toujours, le code est disponibleover on GitHub.