web-dev-qa-db-fra.com

Pourquoi le bloc d'arrêt try-with-resources est-il sélectif?

J'ai lu que le bloc catch dans try-with-resources est facultatif. J'ai essayé de créer un objet Connection dans un bloc try-with-resources, sans aucun bloc catch, uniquement pour obtenir une erreur de compilation d'Eclipse: "Le type d'exception non gérée SQLException est généré par l'invocation automatique de close()."

Étant donné que chaque ressource pouvant être utilisée dans try-with-resources implémente AutoCloseable et lève donc potentiellement une exception lors de l'invocation de la méthode close(), je ne comprends pas comment la clause catch est facultative, car elle ne me permet pas de sauter l'exception de close()

Existe-t-il une exigence particulière selon laquelle l'implémentation spécifique de AutoCloseable ne déclare pas directement une exception renvoyée dans sa méthode close()? (par exemple, remplacez close() throws Exception de AutoCloseable par un close() qui ne lève aucune exception)? 

..ou est-ce éventuellement juste un problème Eclipse?

Edit: Voici le fragment de code le plus simple qui provoque encore le problème:

try (Connection con = dataSource.getConnection()) {
  /*...*/

}

Vous pensez si cela est lié ou non à l'utilisation d'un JNDI DataSource?

Merci d'avance. 

22
Mer

Il est facultatif si close() n'est pas en mesure de générer une exception vérifiée. Toutefois, si close() peut le faire, une exception vérifiée devrait être gérée de manière normale, soit avec un bloc catch, soit en lançant à partir de la méthode dans laquelle se trouve le bloc try-with-resources

Plus de détails sont dans JLS 14.2.3

14.20.3.2. Essai étendu avec ressources

Une instruction try-with-resources avec au moins une clause catch et/ou une clause finally est appelée une instruction try-with-resources étendue.

La signification d'une instruction try-with-resources étendue:

try ResourceSpecification
    Block
[Catches]
[Finally]

est donné par la traduction suivante en une instruction try-with-resources de base imbriquée dans une instruction try-catch ou try-finally ou try-catch-finally:

try {
    try ResourceSpecification
       Block
}
[Catches]
[Finally]

La traduction a pour effet de placer la spécification de ressource "à l'intérieur" de l'instruction try. Cela permet à une clause catch d'une instruction try-with-resources étendue d'attraper une exception en raison de l'initialisation ou de la fermeture automatique d'une ressource.

De plus, toutes les ressources auront été fermées (ou tentées de l'être) au moment de l'exécution du dernier bloc, conformément à l'intention du mot-clé finally. 

Vous pensez si cela est lié ou non à l'utilisation d'une source de données JNDI?

Oui, ça l'est. 

Dans l'exemple de bloc try-with-resourses que vous avez fourni, il est nécessaire d'intercepter l'exception et de gérer, ou de rejeter de la méthode dans laquelle se trouve le bloc, car SQLException est une exception contrôlée. 

17
jdphenix

Vous pouvez simplement lancer l'exception (ou la capturer dans un autre bloc try-catch):

private static void test() throws IOException {
    try(InputStream is = new FileInputStream("test.txt")) {
        while(is.read() > -1) {
        }
    } finally {
        // Will get executed, even if exception occurs
        System.out.println("Finished");
    }
}
2
Robby Cornelissen

Toutes les classes Java (!) Ne lève pas une exception. Parfois, vous souhaitez simplement utiliser une ressource d’essai avec ressources pour utiliser la fonction de fermeture automatique, et rien d’autre.

BufferedReader br = new BufferedReader(new FileReader(path));
try {
    return br.readLine();
} finally {
    if (br != null) br.close();
}

Cette capture est facultative car readLine () ne lève pas d'exception (cochée).

Oui, close () peut générer une exception, mais try-with-resources le gère également.

try (BufferedReader br = new BufferedReader(new FileReader(path))) {
    return br.readLine();
} 

Donc, cet essai de ressources n'a pas besoin d'un piège.

0
markspace

Vous pouvez créer un AutoClosable ne nécessitant pas de bloc catch explicite en déclarant la méthode close () de votre AutoClosable sans aucune exception ou avec une exception RuntimeException. Sans aucune exception, il est clair qu'aucun catch-block n'est requis. De plus, le compilateur ne vérifie pas statiquement qu'une exception RuntimeException soit interceptée (contrairement aux exceptions cochées).

Exemple:

public class AutoClosableDemo
{

    public static void main( final String[] args )
    {
        try (MyAutoCloseable1 mac1 = new MyAutoCloseable1())
        {
            System.out.println( "try-with-resource MyAutoCloseable1" );
        }
        try (MyAutoCloseable2 mac2 = new MyAutoCloseable2())
        {
            System.out.println( "try-with-resource MyAutoCloseable2" );
        }
        // The following is not allowed, because
        // "Unhandled exception type Exception thrown by automatic close() invocation on mac3"
        // try (MyAutoCloseable3 mac3 = new MyAutoCloseable3())
        // {
        // System.out.println( "try-with-resource MyAutoCloseable13" );
        // }
        System.out.println( "done" );
    }

    public static class MyAutoCloseable1 implements AutoCloseable
    {
        @Override
        public void close()
        {
            System.out.println( "MyAutoCloseable1.close()" );
        }
    }

    public static class MyAutoCloseable2 implements AutoCloseable
    {
        @Override
        public void close() throws RuntimeException
        {
            System.out.println( "MyAutoCloseable2.close()" );
        }
    }

    public static class MyAutoCloseable3 implements AutoCloseable
    {
        @Override
        public void close() throws Exception
        {
            System.out.println( "MyAutoCloseable3.close()" );
        }
    }
}
0
mihca

Vous pouvez vérifier le JLS mais il y a en fait un raisonnement relativement simple pour expliquer que c'est la seule façon correcte dont la langue devrait se comporter.

La règle principale des exceptions vérifiées est que toute exception vérifiée déclarée par une méthode doit être gérée, soit en l'attrapant, soit en laissant la méthode appelante la renvoyer.

Try-with-resources appelle toujours (implicitement) la méthode close.

Donc, si la méthode de fermeture spécifique de l’AutoClosable que vous utilisez (déterminée par le type déclaré dans try) déclare lancer une exception vérifiée telle qu’une exception SQLException, vous devez gérer cette exception vérifiée quelque part, sinon il serait possible de enfreindre la règle!

Si la méthode close not ne déclare pas qu'elle lève une exception vérifiée, la règle n'est pas violée et vous n'avez pas besoin de gérer une exception vérifiée pour appeler de manière implicite la méthode close. Il s'agit en réalité d'un échec de compilation si vous essayez de capturer une exception vérifiée qui n'est jamais déclarée comme étant levée.

0
Henno Vermeulen