3.5. Regarding Scope

The same basic scoping rules (i.e., what can be seen within a method when curly braces are involved) that you are used to for if-statements and loops also apply to try-blocks and catch-blocks. If you declare a variable inside a try-block or catch-block, then its scope only extends to what is inside that block.

Test Yourself

Take a few moments to review the code below. The code currently will not compile. Can you find the issue?

Hint: it’s related to scoping!

package cs1302.scope;

import java.io.File;
import java.io.PrintWriter;
import java.io.FileNotFoundException;

public class Example {
   public static void main(String[] args) {

      try {
         String filename = args[0];
         File file = new File(filename);
      } catch (ArrayIndexOutOfBoundsException boundsException) {
         System.err.println("first command-line argument is missing");
      } // try

      try {
         PrintWriter output = new PrintWriter(file);
      } catch (FileNotFoundException notFoundException) {
         System.err.print("unable to open file for writing: ");
         System.err.println(notFoundException.getMessage());
      } // try

   } // main
} // Example

Now, compile and run the code on Odin. Since the code is in a named package (cs1302.scope), make sure to create proper package directories.

Did the compiler give you the expected error message?

Test Yourself Solution and Explanation (Expand when ready)

After properly compiling the provided code example, the compiler will emit a cannot find symbol error similar to the following:

 src/cs1302/scope/Example.java:22: error: cannot find symbol
          PrintWriter output = new PrintWriter(file);
                                               ^
symbol:   variable file
location: class Example
1 error

Pay close attention to the symbol the compiler is unable to find (above the ^ character). It can’t find the variable named file even though we declared it. However, since the variable file is declared inside the try-block, its scope only extends to subsequent lines within the try-block. In other words, the variable can’t be used outside of the try-block, as illustrated below:

try {
   String filename = args[0];
   File file = new File(filename);
   // <---- ✓ scope of `file` extends to this line
   // <---- ✓ and this line
} catch (ArrayIndexOutOfBoundsException boundsException) {
   System.err.println("first command-line argument is missing");
   // <---- ✗ but NOT this line
} // try
// <---- ✗ NOR this line
// <---- ✗ nor any of the lines below

Note

Issues like simple typos, missing import statements, and even an incorrect classpath often causes the Java compiler to emit the cannot find symbol; however, the cause of this particular cannot find symbol error is related to the scope of the symbol (the variable file), which does not extend to a specific line of code that attempts to use that symbol, as indicated by the error message.

Fixing the Problem

There are two high-level strategies for dealing with this kind of scoping issue. You should be aware of the first strategy but you should always try to use the second strategy as it leads to more elegant solutions that are easier to program. These two strategies are outlined below:

Strategy 1
  1. Increase the symbol’s scope by declaring and initializing it on a line that precedes the enclosing try-block and changing the original declaration to a simple assignment – this strategy is also sometimes used to fix similar scoping issues with variables declared in if-statements and loops. If you can extend the variable’s scope to the line that uses the symbol, then the compiler will be able to find it. This strategy does come at a cost:

    • The code that uses the variable after the try-catch cannot and should not assume the code in the try-block is ever executed as a thrown exception may cause it to get skipped. For example, if you initialize a reference variable to null before a try-catch and reassign it to something that’s not null within the try-block, then the code after the try-catch needs to account for the possibility that the variable was never reassigned and is still null. If it does not, then it runs the risk of throwing an unchecked NullPointerException during runtime.

      public static void main(String[] args) {
      
         File file = null; // <--- moving the declaration up extends the scope
      
         try {
            String filename = args[0];
            file = new File(filename);
         } catch (ArrayIndexOutOfBoundsException boundsException) {
            System.err.println("first command-line argument is missing");
         } // try
      
         try {
            PrintWriter output = new PrintWriter(file); // <--- risk NullPointerException
         } catch (FileNotFoundException notFoundException) {
            System.err.print("unable to open file for writing: ");
            System.err.println(notFoundException.getMessage());
         } // try
      } // main
      
Strategy 2 (Preferred)
  1. Place code that depends (or uses) on the symbol/variable within the try-block since it will be skipped if an exception occurs within the try-block before that line (as execution flows to a corresponding catch block).

    • This strategy often requires changes to multiple lines of code; however, it also often leads to a more elegant solution, as illustrated below:

    public static void main(String[] args) {
    
       try {
          String filename = args[0];
          File file = new File(filename);
    
          // moved the line below into the try since it depends on the file variable.
          // If an exception happens in one of the two lines above, this next line is skipped.
          PrintWriter output = new PrintWriter(file);
    
       } catch (ArrayIndexOutOfBoundsException boundsException) {
          System.err.println("first command-line argument is missing");
       } catch (FileNotFoundException notFoundException) {
          System.err.print("unable to open file for writing: ");
          System.err.println(notFoundException.getMessage());
       } // try
    } // main
    
Video Example

Here is a short video demonstrating these concepts:

https://www.youtube.com/watch?v=DlmrBF7pP1k

IMAGE ALT TEXT