web-dev-qa-db-fra.com

Pourquoi un bloc Try / Catch crée-t-il une nouvelle étendue de variable?

Par exemple:

try
{
    SomeObject someObject = new SomeObject();
    someObject.dangerousMethod();
}
catch(Exception e)
{
}
someObject.anotherMethod(); //can't access someObject!

Mais vous pouvez le déclarer avant le try/catch bloquer et puis ça marche bien:

SomeObject someObject;
try
{
    someObject = new SomeObject();
    someObject.dangerousMethod();
}
catch(Exception e)
{
}
someObject.anotherMethod(); //works fine

Je me demande simplement la raison du design. Pourquoi les objets sont-ils créés dans le try/catch le bloc n'est pas dans la portée avec le reste de la méthode? Peut-être que je ne comprends pas au fond comment un try/catch fonctionne en plus de simplement regarder Exceptions levé.

43
trevor-e

Pourquoi les objets créés dans le bloc try/catch ne sont-ils pas dans la portée du reste de la méthode?

Elles sont. Les variables déclarées dans le bloc try/catch Ne sont pas dans la portée du bloc conteneur, pour la même raison que toutes les autres déclarations de variables sont locales à la portée dans laquelle elles se produisent: C'est ainsi que la spécification la définit. :-) (Plus ci-dessous, y compris une réponse à votre commentaire.)

Voici un objet créé dans un try/catch Qui est accessible en dehors de celui-ci:

SomeObject someObject = null;
try
{
    someObject = new SomeObject();
    someObject.dangerousMethod();
}
catch(Exception e)
{
}
someObject.anotherMethod(); // This is fine -- unless the SomeObject
                            // constructor threw the exception, in which
                            // case someObject will be null

Notez la différence. Lorsque la variable est déclarée définit la portée dans laquelle elle existe, pas où l'objet a été créé .

Mais sur la base des noms de méthode et autres, la structure la plus utile serait:

SomeObject someObject = new SomeObject();
try
{
    someObject.dangerousMethod();
}
catch(Exception e)
{
}
someObject.anotherMethod();

Re votre commentaire:

Je suppose que je ne comprends pas pourquoi une autre étendue a même été créée pour un bloc try/catch.

En Java, tous les blocs créent une portée. Le corps d'un if, le corps d'un else, d'un while, etc. - ils créent tous une nouvelle portée de variable imbriquée:

if (foo) {
    SomeObject bar = new SomeObject();
}
bar.doSomething(); // <== Compilation error, `bar` is not defined

(En fait, même un bloc sans aucune structure de contrôle en crée un.)

Et si vous y réfléchissez, cela a du sens: certains blocs sont conditionnels, comme celui définissant le corps d'un if ou while. Dans ce qui précède if, bar peut ou non avoir été déclaré (selon la valeur de foo), ce qui n'a aucun sens car bien sûr le compilateur n'a pas de concept de la valeur d'exécution de foo. Donc, probablement pour des raisons de cohérence, les concepteurs de Java est allé de pair avec tous les blocs créent une nouvelle portée imbriquée. (Le concepteur de JavaScript est allé dans l'autre sens - il n'y a pas du tout d'étendue de bloc, bien qu'il soit ajouté - et cette approche également confond les gens.)

49
T.J. Crowder

En Java, chaque fois que vous disposez d'un { } paire, vous pouvez créer une nouvelle étendue.

Considérer ce qui suit

class ScopeTest {
    public static void main(String[] args) {
        int i = 0;
        { int j = 0; System.out.println(j); }
        { int j = 2; System.out.println(j); }
    }
}

Le try/catch suit juste cet idiome et applique un { } paire à créer.

Pour répondre à votre suivi d'une déclaration if non entre crochets, pensez à:

class MultiRTree {
    public static void main(String...args) {
        boolean b = args.length == 0;
        if(b) String s = new String("hello");
    }
}

résulte en

c:\files\j>javac ScopeTest.Java
ScopeTest.Java:4: not a statement
        if(b) String s = new String("hello");
              ^
ScopeTest.Java:4: ';' expected
        if(b) String s = new String("hello");
                    ^
2 errors

Cependant, cela se compilera très bien.

class ScopeTest {
    public static void main(String...args) {
        boolean b = args.length == 0;
        if(b) new String("hello");
    }
}

Pourquoi en est-il ainsi, selon le chapitre 14 de JLS, section 9, si est défini comme:

IfThenStatement:
    if ( Expression ) Statement

Et l'instruction est définie comme (14.5)

Statement:
    StatementWithoutTrailingSubstatement
    LabeledStatement
    IfThenStatement
    IfThenElseStatement
    WhileStatement
    ForStatement

StatementWithoutTrailingSubstatement:
    Block
    EmptyStatement
    ExpressionStatement
    AssertStatement
    SwitchStatement
    DoStatement
    BreakStatement
    ContinueStatement
    ReturnStatement
    SynchronizedStatement
    ThrowStatement
    TryStatement

Donc, un bloc, une expression ou une instruction vide sont très bien. Mais une déclaration (définie au chapitre 6) n'est pas dans la grammaire de la déclaration.

7
corsiKa

La portée d'une variable ou d'un objet se trouve dans la portée (définie par des accolades {}) dans laquelle elle est définie.

Étant donné que try catch lance une nouvelle portée où une erreur peut être levée de sorte que les objets définis à l'intérieur de try catch ne sont pas disponibles en dehors de sa portée.

5
SiB

try/catch crée une nouvelle portée pour la simple raison qu'il s'agit d'un élément de niveau bloc. En fait, il suffit de placer {} juste au hasard dans une méthode créera un nouveau bloc de code avec sa propre portée locale.

3
Tim Bender

Chaque fois que vous utilisez une parenthèse '{', vous exprimez une nouvelle portée en C++ et en Java. Vous essayez d'essayer une opération nécessite une configuration interne et l'étendue des noms permet de sauter rapidement hors du bloc d'essai sans beaucoup de nettoyage.

Certaines langues vous permettent d'accéder à ces variables de portée en dehors de la portée tant qu'il n'y a pas de conflit de nom (comme en Python), mais cela nécessite une structure de pile interne légèrement différente et pourrait quand même augmenter les coûts de la capture d'essai.

C'est aussi la façon dont les définitions de portée sont définies dans Java - comme l'ont souligné de nombreuses autres réponses.

3
Pyrce