58

例えば:

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が、メソッドの残りの部分のスコープにないのはなぜですか? たぶん、投げられるtry/catchのを見るだけでなく、 a がどのように機能するかを深く理解していないのかもしれません。Exceptions

4

5 に答える 5

59

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

違いに注意してください。変数宣言されている場所は、オブジェクト作成された場所ではなく、変数が存在するスコープを定義します。

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

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

あなたのコメントについて:

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

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

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

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

考えてみれば、それは理にかなっています。 iforの本体を定義するブロックのように、一部のブロックは条件付きwhileです。上記のifでは、bar( の値に応じてfoo) が宣言されている場合と宣言されていない場合がありますが、もちろんコンパイラには の実行時値の概念がないため、これは意味がありませんfoo。おそらく一貫性を保つために、Java の設計者は、すべてのブロックが新しいネストされたスコープを作成するようにしました。( JavaScriptの設計者は別の方法をとっています。ブロック スコープは追加されていますが、まだまったくありません。また、このアプローチは人々を混乱させます。)

于 2012-07-25T17:11:01.150 に答える
10

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章で定義)はステートメントの文法には含まれていません。

于 2012-07-25T17:14:43.513 に答える
5

角かっこ「{」を使用するたびに、C++とJavaの両方で新しいスコープを表現しています。操作を試行するには、内部設定が必要です。名前をスコーピングすると、多くのクリーンアップを行わなくても、tryブロックからすばやくジャンプして戻ることができます。

一部の言語では、名前の競合がない限り(Pythonのように)、スコープ外のスコープ変数にアクセスできますが、これにはわずかに異なる内部スタック構造が必要であり、それでもtrycatchのコストが増加する可能性があります。

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

于 2012-07-25T17:18:39.400 に答える
5

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

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

于 2012-07-25T17:12:46.410 に答える
3

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

于 2012-07-25T17:14:53.590 に答える