Scope

Learning Goals

By the end of this lesson I will be able to:

  • Identify what scope is and the different levels of scope that a variable can have
  • Demonstrate ability to define variables in proper scope

Basics of Scope

Has this ever happened to you? You're creating some super awesome code and you try to access a variable only to have the IDE tell you "myThing cannot be resolved to a variable." You know you created it. Heck you can even find the line where it's defined. And the one that sets it to a value! But the IDE still doesn't know what it is.

Frustrating! Why can't I just access any variable at any time anywhere? Well it all comes down to variable scope.

Variable Scope is how the programming language decides in which parts of your program a variable exists. If you try to access a variable outside of its scope, the compiler will generate an error.

A few things to remember:

  • The easiest way to determine a scope is to notice which pair of curly braces { } a variable is inside. That usually determines the scope.
  • A variable that does not exist in this way is said to be "out of scope"

Levels of Scope for Accessing Variables

We have 3 different levels of scope:

  • Block level - Most restrictive
  • Local (or Method) level - Most common
  • Class (or Global) level - Most general

Block Level Scope

Any variable that is defined inside a for loop, while loop or if statement only exists inside that statement. For example:

1 ...
2      if (true) { // Beginning of Block level Scope
 
3          // Defining a variable indside Block Level Scope
4          int x = 1;
 
5          // Accessing the variable -- Totally fine. The variable x exists here
6          System.out.println("x is: " + x);
 
7      } // End of Block Level Scope
 
8      // Accessing the variable again -- Nope! The variable x doesn't exist anymore! We've left Block Level Scope.
9      System.out.println("x is: " + x); // "x cannot be resolved to a variable" -- Sincerely, Eclipse
10 ...

In the above example, we create and assign a value to the variable x (an integer) on line 4, use it on line 6. Perfectly fine because the block level scope starts on line 2 and ends on line 7. The problem exists when we try to access x on line 9. At this point, x is out of scope. In fact, as far as Java is concerned, x never even existed. Poof! Gone! What's x?

If the variable is created before the beginning of block level scope, then this whole problem goes away.

1 ...
2      // Defining a variable BEFORE Block Level Scope begins
3      int x = 1;

4      if (true) { // Beginning of Block level Scope
 
5          // Accessing the variable -- Totally fine. The variable x exists here
6          System.out.println("x is: " + x);
 
7      } // End of Block Level Scope
 
8      // Accessing the variable again -- All good! The variable x was defined before block 
9      //    level scope began, so it exists after it ends.
10     System.out.println("x is: " + x);
11 ...

This example illustrates the idea that the curly braces determine the scope of the variable. Speaking of which; you can create scope artificially using curly braces:

1 ...
2      if (true) { // Beginning of Outer Block level Scope
 
3          { // Beginning of Inner Block Level Scope
4              // Defining a variable inside Block Level Scope
5              int x = 1;
6          } // End of Inner Block Level Scope

7          // Accessing the variable -- Uh oh. This is broken now...
8          System.out.println("x is: " + x); // "x cannot be resolved to a variable" -- Sincerely, Eclipse
 
9      } // End of Outer Block Level Scope
 
10     // Accessing the variable again -- Nope! The variable x doesn't exist anymore! We've left Block Level Scope.
11     System.out.println("x is: " + x); // "x cannot be resolved to a variable" -- Sincerely, Eclipse
12 ...

Now we've gone and done it... Variable x no longer exists on line 8. Be careful with your variable scope.

Local (Method) Level Scope

Remember methods (or procedures or functions) are chunks of code that can be called at any time from anywhere (mostly). Let's consider the following Java program:

import java.lang.Math;

public class ExtendedMath {
    public static void main (String[] args) {
        double a = 3.0;
        double b = 4.0;
        double c = findHypotenuse();

        // Use a, b and c in a print line statement. All good! Variables a, b and c were declared here.
        System.out.println("A triangle with sides " + a + ", and " + b + " has hypotenuse " + c);

        // Use the variable sidesSquared. Nope! Variable sidesSquared was defined in findHypotenuse(). Not here.
        System.out.println("The sides squared total: " + sidesSquared); // "sidesSquared cannot be resolved to a variable"
    }

    public static double findHypotenuse () {
        // Use variables a and b to calculate the sides squared. Nope! 
        //    Even though a and b both existed BEFORE findHypotenuse() was called, this is a new entry on
        //    Java's "callstack" so it doesn't know what they are. They don't exist here.
        //        (more on the callstack later when we cover recursion) 
        double sidesSquared = a^2 + b^2;
        
        // Use sideSquared to do a math calculation. Fine. The variable was created within this method (locally).
        double hypotenuse = Math.sqrt(sidesSquared);

        return hypotenuse;
    }
}

From the above, we can see that any variables declared in one method cannot be used by the other. Also note that parameters have the same scope as local/method variables.

Exercise: There are multiple ways to fix findHypotenuse code. How many can you find? Which one do you think makes the most sense?

Class (Global) Level Scope

Disclaimer: There is a difference between class level variables and global variables which we're going to ignore right now.

Global variables are accessible everywhere within a class. See below:

public class Thingamabob {

    static int someValue;

    public static void main (String [] args) {
        // Assign someValue the integer 1. All good here.
        someValue = 1;

        // Still good.
        System.out.println("Our value starts at: " + someValue); // Prints "Our value starts at: 1"

        foo();

        // Still good.        
        System.out.println("Now our value is: " + someValue); // Prints "Now our value is: 3"

        bar();

        // Still good. And we're done!
        System.out.println("And finally it is: " + someValue); // Prints "And finally it is: 15"
    }

    public static void foo() {
        // Multiply someValue by 3 and store the result into someValue. Yep. No problem.
        someValue *= 3;
    }

    public static void bar() {
        // Add 2 to some value and store it again. Cool.
        someValue += 2;

        foo();
    }
}

Declaring Variables

At this point we've covered what happens when you try to access a variable outside of scope, but what about declaring 2 variables with the same name? Can it be done? The answer is... Sometimes. It depends on what your scope of declaration. Consider the following:

1  class Test
2  {
3      // Global variable x.
4      static int x = 5;
5    
6      public static void main(String args[])
7      {
8          // This is allowed. There is a global x and a local x.
9          int x = 10;
10     
11         System.out.println(x);      // Prints 10
12         System.out.println(Test.x); // Prints 5
13   
14         x = 1;        // Changes the local x to 1
15         Test.x = 100; // Changes the global x to 100
16   
17         // Declaring i here. No problem. It doesn't exist yet so we're all good.
18         int i = 10;
19   
20         // And we can use it without issue either.
21         System.out.println("I'm going to count to " + i);
22    
23         // Our loop declares i. That's a problem. Notice that both line 18 and line 26
24         //    fully declare the variable "int i = ..." which causes a conflict
25         //    on the next line. That's an error.
26         for (int i = 0; i < 10; ++i) {
27         
28             // At this point Java doesn't know which i you are referring to.
29             System.out.println(i);
30         }
31     }
32 }

Exercises:

1. Predict the output of the following code. If the code produces an error, fix it without removing functionality and predict the output.

a.

class Test
{
    public static void main(String args[])
    {
        for (int x = 0; x < 4; x++)
        {
            System.out.println(x);
        }
 
        System.out.println(x);
    }
}

b.

class Test
{
    public static void main(String args[])
    {
        int a = 5;
        for (int a = 0; a < 5; a++)
        {
            System.out.println(a);
        }
    }
}

c.

class Test
{
    int x = 25;

    public static void main(String args[])
    {
        {
            int x = 5;
            {
                int x = 10;
                System.out.println("The global x value is: " + x);
            }
        }
    }
}

2. Mark where d, e, i and j exist by commenting the line on which they start and end. The variable i is done as an example.

public class ScopeTest {
    public static void main(String[] args) {
        int i= 1;                                          // i begins here
        double d= 0.0;
        for (int j= 0; j < 5; j++) {
            double e= j;
            d += i;
            e += j;
            System.out.println("d: "+d+" e: "+e);
        }
        if (d > 0) {
            int j= 2;
            double e= 4.0;
            System.out.println("If line d: "+d+" e: "+e);
        }
        double e= 0.0;
        e += d + i;
        System.out.println("Last line d: "+ d+" e: "+e); 
    }                                                      // i ends here
}