61

For example:

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

But you can declare it before the try/catch block and then it works fine:

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

I'm just wondering the design reason for this. Why are Objects created within the try/catch block not in scope with the rest of the method? Maybe I'm not understanding deep down how a try/catch works besides just watching for Exceptions thrown.

telkins
  • 10,440
  • 8
  • 52
  • 79
  • 22
    Every block in Java defines a new scope. Not just try blocks. It would be very inconsistent not to follow the general rule. – JB Nizet Jul 25 '12 at 17:15

5 Answers5

61

Why are Objects created within the try/catch block not in scope with the rest of the method?

They are. Variables declared within the try/catch block are not in scope in the containing block, for the same reason that all other variable declarations are local to the scope in which they occur: That's how the specification defines it. :-) (More below, including a reply to your comment.)

Here's an object created within a try/catch which is accessible outside of it:

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

Note the difference. Where the variable is declared defines the scope in which it exists, not where the object was created.

But based on the method names and such above, the more useful structure for that would be:

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

Re your comment:

I guess I'm confused as to why another scope has even been created for a try/catch block.

In Java, all blocks create scope. The body of an if, the body of an else, of a while, etc. — they all create a new, nested variable scope:

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

(In fact, even a block without any control structure creates one.)

And if you think about it, it makes sense: Some blocks are conditional, like the one defining the body of an if or while. In the above if, bar may or may not have been declared (depending on the value of foo), which makes no sense because of course the compiler has no concept of the runtime value of foo. So probably for consistency, the designers of Java went with having all blocks create a new nested scope. (The designer of JavaScript went the other way — there is no block scope at all, yet, though it's being added — and that approach also confuses people.)

T.J. Crowder
  • 1,031,962
  • 187
  • 1,923
  • 1,875
  • 1
    Beat me to it! This scope is to protect against exceptions being thrown (even within constructors of objects being declared). We can't continue using the object if the try never got to that instantiation statement. – David B Jul 25 '12 at 17:12
  • 1
    I guess I'm confused as to why another scope has even been created for a try/catch block. – telkins Jul 25 '12 at 17:12
  • 3
    I think he knows how it works; rather he was asking why the decision to do it that way. – corsiKa Jul 25 '12 at 17:12
  • @Atlos: Because in Java, all blocks create scope. The body of an `if`, the body of an `else`, of a `while`, etc. All create their own variable scope. That's just how the language is defined. It has the advantage that you can declare things closer to where they're used; it has the disadvantage that doing so can lead you to write long functions when really you should break them up into sub-functions. :-) – T.J. Crowder Jul 25 '12 at 17:15
  • @T.J.Crowder That makes more sense then to keep all blocks consistent. I'm finding it tedious having to declare all my variables outside of the block if I want to have it assigned a value from a method that throws an Exception and then later use it somewhere else. Guess there isn't a way around it. – telkins Jul 25 '12 at 17:20
  • @Atlos: Nope, no way around it, if you want to use it outside the `try/catch`, you need to declare it outside the `try/catch`. If you're finding declaring all of your variables at the top of the function (frequently cited as best practice, not strictly required) tedious, it could indicate that your functions are getting too long. (I am the **master** of over-long functions, trying to reform.) :-) – T.J. Crowder Jul 25 '12 at 17:25
  • Thanks. I will accept this as the most complete answer, although corsiKa had a nice example. – telkins Jul 25 '12 at 17:33
  • You can even create a new scope without a keyword; `{ SomeObject bar; }` is perfectly legal, and `{ SomeObject bar; } bar = new SomeObject();` will fail to compile because `bar` is out of scope. – BlueRaja - Danny Pflughoeft Jul 25 '12 at 19:17
  • @BlueRaja-DannyPflughoeft: Quite true. – T.J. Crowder Jul 25 '12 at 21:11
  • @corsiKa, then his question is answered by [David B](http://stackoverflow.com/questions/11655020#comment-15443807). – Franklin Yu Jan 29 '16 at 02:46
9

In Java, any time you have a { } pair, you can create a new scope.

Consider the following

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

The try/catch just follows this idiom, and enforces a { } pair to be created.

To respond to your followup of a non-bracketed if statement, consider:

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

results in

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

However, this will compile just fine.

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

Why this is so, according to the JLS Chapter 14, section 9, if is defined as:

IfThenStatement:
    if ( Expression ) Statement

And Statement is defined as (14.5)

Statement:
    StatementWithoutTrailingSubstatement
    LabeledStatement
    IfThenStatement
    IfThenElseStatement
    WhileStatement
    ForStatement

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

So a block, expression statement, or empty statement those are just fine. But a declaration (defined in chapter 6) is not in the grammar of statement.

corsiKa
  • 81,495
  • 25
  • 153
  • 204
  • What about if you had an `if` statement one-liner where you didn't use the `{ }` pair? Such as `if(true) SomeObject someObject = new SomeObject()`, would `someObject` be in scope of the method or does the compiler insert the implied brackets for me? – telkins Jul 25 '12 at 17:26
  • @Atlos - it does some awkward stuff. Suffice it to say, it's not allowed! I'll edit it in. – corsiKa Jul 25 '12 at 17:28
  • 1
    Basically, `new SomeObject();` is allowed, because it's a statement, but `SomeObject so = new SomeObject()` is not allowed because it's a declaration (even though it contains a statement in it). – corsiKa Jul 25 '12 at 17:36
5

Every time you do use a bracket '{' you're expressing a new scope in both C++ and Java. You're attempt to try an operation requires some internal setup and scoping the names allows for quick jumps back out of the try block without a lot of cleanup.

Some languages will let you access those scoped variables outside of the scope as long as there isn't a name conflict (like in Python), but this requires an slightly different internal stack structure and could still increase the costs of the try catch regardless.

Also it's just how scope definitions are defined in Java -- as many of the other answers pointed out.

Pyrce
  • 8,296
  • 3
  • 31
  • 46
4

The scope of a variable or object is in the scope (defined by curly braces {}) in which it is defined.

Since try catch initiates a new scope where some error can be thrown so the objects defined inside try catch are not available outside it's scope.

Bharat Sinha
  • 13,973
  • 6
  • 39
  • 63
2

try/catch creates a new scope for the simple reason that it is a block level element. In fact, simply placing {} just randomly inside a method will create a new block of code with it's own local scope.

Tim Bender
  • 20,112
  • 2
  • 49
  • 58