web-dev-qa-db-ja.com

Try / Catchブロックが新しい変数スコープを作成するのはなぜですか?

例えば:

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

ただし、try/catchブロックの前に宣言すると、正常に機能します。

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

私はこれの設計上の理由を疑問に思っています。 try/catchブロック内で作成されたオブジェクトが、メソッドの他の部分のスコープ内にないのはなぜですか?たぶん、Exceptionsがスローされるのを監視する以外に、try/catchがどのように機能するかを深く理解していないのかもしれません。

43
trevor-e

Try/catchブロック内で作成されたオブジェクトが、メソッドの他の部分のスコープ内にないのはなぜですか?

彼らです。 変数宣言済みtry/catchブロック内は、すべてが同じ理由で、包含ブロックのスコープ内にありません他の変数宣言は、それらが発生するスコープに対してローカルです。それが仕様で定義されている方法です。 :-)(コメントへの返信を含む、以下の詳細。)

以下は、外部でアクセス可能なtry/catch内に作成されたオブジェクトです。

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

違いに注意してください。 variabledeclaredである場合、それが存在するスコープを定義します。ここで、objectcreatedでした。

しかし、上記のメソッド名などに基づいて、より便利な構造は次のようになります。

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

あなたのコメントを再:

Try/catchブロック用に別のスコープが作成された理由について混乱していると思います。

Javaでは、すべてのブロックがスコープを作成します。 ifの本体、elseの本体、whileの本体など。これらはすべて、新しいネストされた変数スコープを作成します。

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

(実際、制御構造のないブロックでも作成されます。)

考えてみると、それは理にかなっています:いくつかのブロックは、ifまたはwhileの本体を定義するような条件付きです。上記のifで、barは(fooの値に応じて)宣言されている場合と宣言されていない場合があります。 fooのランタイム値。そのため、おそらく一貫性のために、Javaの設計者はallブロックに新しいネストされたスコープを作成させました。JavaScriptの設計者他の方法で行きました—ブロックスコープはまだありませんが、追加されています—そしてそのアプローチalso人を混乱させる。)

49
T.J. Crowder

Javaでは、{ }ペア、新しいスコープを作成できます。

以下を考慮してください

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); }
    }
}

Try/catchはこのイディオムの後に続き、{ }作成されるペア。

括弧で囲まれていないifステートメントのフォローアップに対応するには、以下を考慮してください。

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

結果として

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

ただし、これは問題なくコンパイルされます。

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

JLSの第14章のセクション9によると、次のように定義されている場合

IfThenStatement:
    if ( Expression ) Statement

ステートメントは(14.5)として定義されます

Statement:
    StatementWithoutTrailingSubstatement
    LabeledStatement
    IfThenStatement
    IfThenElseStatement
    WhileStatement
    ForStatement

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

したがって、ブロック、式ステートメント、または空のステートメントは問題ありません。ただし、宣言(第6章で定義)は文の文法には含まれません。

7
corsiKa

変数またはオブジェクトのスコープは、それが定義されているスコープ(中括弧{}で定義)内にあります。

Try catchはエラーをスローできる新しいスコープを開始するため、try catch内で定義されたオブジェクトはそのスコープ外では使用できません。

5
SiB

try/catchは、ブロックレベルの要素であるという単純な理由で新しいスコープを作成します。実際、単に{}メソッド内でランダムに実行すると、独自のローカルスコープを持つ新しいコードブロックが作成されます。

3
Tim Bender

ブラケット '{'を使用するたびに、C++とJavaの両方で新しいスコープを表現しています。操作を試行しようとすると、内部設定が必要になり、名前をスコープすることで、多くのクリーンアップなしでtryブロックからすばやくジャンプできます。

一部の言語では、名前の競合が発生しない限り(Pythonなど)、スコープ外のスコープ変数にアクセスできますが、これには少し異なる内部スタック構造が必要であり、それでもなお、try catchのコストが増加する可能性があります。

また、Java-他の多くの回答が指摘しているように、スコープ定義がどのように定義されているかということです。

3
Pyrce