Java 11 Developer Certification - Scope

September 28, 2020

What we are covering in this lesson

  1. Definition of scope
  2. Class variable scope
  3. Instance variable scope
  4. Method variable scope
  5. Block scope
  6. Loop scope
  7. Shadowing
  8. Scope in nested or inner classes

Definition of scope in Java

The scope of declaration is the region of the program, within which the entity (e.g.: declared variable) declared by the declaration can be referred to using a simple name, given, that it is visible.

Now, that’s a bit cryptic, let break it down thru examples and see what kind of scopes we are talking about when programming in Java.

There are scopes where we can “qualify” the variables and there are other scopes where we simply can’t. Here is the list of them:

  • Scope of variables that can be qualified

    • Instance (this)
    • Class (Classname)
    • Inner class (implicit)

  • Scope of variables that cannot be qualified

    • Method
    • Loop and loop block
    • Block (including exception block)

Class variable scope

The Class scope is valid for static members of classes.

package vehicle.motorbike.ducati;

public class Streetfighter {

    static int MODELYEAR = 2016;
    private int nextService;

    public Streetfighter(){
     this.nextService = MODELYEAR + 4;
    };

    public static void main(String args[]) {
        Streetfighter sf = new Streetfighter();
        System.out.println("Model year: " + Streetfighter.MODELYEAR);
        System.out.println("Next service: " + sf.getNextService());
    }

    public int getNextService() {
        return nextService;
    }
}

By looking at the code snippet above what we can understand is that the class variable - static int MODELYEAR - is available thru the entire class. We also see that we can reference the class variable with and without the class qualifier - Streetfighter. While this is possible to use the static variable without it’s qualilfier, the best practice is to always qualify class variables with the classname.

Another important thing to remember is the anti-pattern to use an instance qualifier to access the static class variable. Again, it’s possible, just do not do it, otherwise you may end up with unnecessary bugs in your code.

Instance variable scope

This is the second variable scope that can be qualified and the qualifier is the well known this keyword. Let’s an example that describes dos and donts.

package vehicle.motorbike.ducati;

public class Streetfighter {

    private String model;
    private int modelYear;

    public Streetfighter(String model, int modelYear){
     model = model;
     this.modelYear = modelYear;
    };

    public static void main(String args[]) {
        Streetfighter sf = new Streetfighter("848", 2016);
        System.out.println("Ducati Streetfighter " + sf.getModel() + ", year: " + sf.getModelYear() );
    }

    public String getModel() {
        return model;
    }

    public int getModelYear() {
        return modelYear;
    }
}

The output is:

Ducati Streetfighter null, year: 2016

Common mistake is to forget to use this keyword in the constructor when trying to initialize instance variables, that leads to self assignments. In our example we did not prefix our instance variable model with this in our constructor, thus the model could never be initialized. The modelYear variable though is correctly qualified in the constructor and that is reflected in the output.

If we make a small modification in the contructor and we prefix the model instance variable correctly:

...
    public Streetfighter(String model, int modelYear){
     this.model = model;
     this.modelYear = modelYear;
    };
...

The output is now as we would normally expect:

Ducati Streetfighter 848, year: 2016

Method variable scope

When we declare a variable in a method, it has method scope and that means that the variable will be valid and visible within the concerned method.

Let us see a snippet with a working example. Assume that we have a method, that’s required to make an appointment at our local garage:

    public void makeServiceAppointment(int leadTime){
        Boolean isReservationMade;

        Garage service = new Garage();
        isReservationMade = service.getAppointment(leadTime);

        if (isReservationMade) {
            System.out.println("We could make the reservation");
        } else {
            System.out.println("We could not make the reservation");
        }
    }

We have 3 variables that have method scope:

  • int leadTime
  • Boolean isReservationMade
  • Garage service

What happens if we try to declare a new local variable - int leadTime = 12; - within the if block:

...
        if (isReservationMade) {
            int leadTime = 12;
            System.out.println("We could make the reservation");
        } else {
...

The error message thrown is like :

C:\Users\darva\IdeaProjects\j11-exam\src\vehicle\motorbike\ducati\Streetfighter.java:34:17
java: variable leadTime is already defined in method makeServiceAppointment(int)

This error message is self-explanatory, we already have a method local leadTime variable and it’s not allowed to declare another one with the same name as the compiler would not know which one to use.

Block (bracket) scope

Using the above snippets I am adding a new variable Date appointmentDate to the if block, this time of course I am not using the same name as the other method variables (leadTime, isReservationMade, service) to avoid any clashes:

    public void makeServiceAppointment(int leadTime){
        Boolean isReservationMade;

        Garage service = new Garage();
        isReservationMade = service.getAppointment(leadTime);

        if (isReservationMade) {
            Date appointmentDate = Date.from(Instant.now().plus(leadTime, ChronoUnit.DAYS));
            System.out.println("We could make the reservation: " + appointmentDate);
        } else {
            System.out.println("We could not make the reservation");
        }
    }

If you were wondering what the Date appointmentDate = Date.from(Instant.now().plus(leadTime, ChronoUnit.DAYS)); does, it simply adds the leadTime (in days) to today’s date. In this case variable appointmentDate has block scope and only accessible within the block fenced by the curly braces around it. The output is something like this:

We could make the reservation: Wed Oct 07 16:02:54 CEST 2020

If we tried to access appointmentDate outside it’s scope, e.g.: in the else block:

...
        if (isReservationMade) {
            Date appointmentDate = Date.from(Instant.now().plus(leadTime, ChronoUnit.DAYS));
            System.out.println("We could make the reservation: " + appointmentDate);
        } else {
            System.out.println("We could not make the reservation until " + appointmentDate);
        }
...

we would get an error message java: cannot find symbol:

C:\Users\darva\IdeaProjects\j11-exam\src\vehicle\motorbike\ducati\Streetfighter.java:41:77
java: cannot find symbol
  symbol:   variable appointmentDate
  location: class vehicle.motorbike.ducati.Streetfighter

Loop variable scope

Loop variables, just like method or block (bracket) variables are only valid and visible within the loop’s body, here is a working example of a loop variable and its valid usage within the loop scope

    public void printNums(){
        for (int i = 0; i < 10; i++) {
            System.out.print(i);
        }
    }

No surprise i is our loop variable and its value gets used within the loop’s body. What happens if we want to create another i variable within the backets (block scope)?

    public void printNums(){
        for (int i = 0; i < 10; i++) {
            int i = 10;
            System.out.print(i);
        }
    }

We get a similar error message as we have already seen in the Method variable scope section, when we tried to re-declare the leadTime variable:

java: variable i is already defined in method printNums()

We simply just can’t create another local variable in a lower scope with the same name, as the compiler would get confused.

Shadowing

In the previous sections we demonstrated that we can’t create a local variable with the same name at a lower scope. However, it’s not always true. If we can qualify a variable (with e.g.:this for an instance variable or [ClassName] for class variables) at a lower scope, we can re-declare it and that’s called shadowing. Shadowing is not a good practice, try to avoid it.

class HelloJava {
    static String salute = "Hello";
    private String what = " World";
    public static void main(String[] args) {
        HelloJava hj = new HelloJava();
        String salute = "Szia";
        String what = " Vilag";
        System.out.println("Shadowed salutation: " + salute + what);
        System.out.println("Qualified salutation: " + HelloJava.salute + hj.what);
    }
}

The output is:

Shadowed salutation: Szia Vilag
Qualified salutation: Hello World

Note that we shadowed both variables (salute and what) in the main method and we simply referenced and printed them (in the Shadowed salutation line).

When we qualified them (HelloJava.salute and hj.what in the Qualified salutation print statement) we got the values from the broader scope. Again, this can really be confusing, do not get into the habit of doing so.

Variable scope in inner or nested classes

We learned so far that if we can’t qualify a local variable, we can’t create another one with the same name at a lower scope. Of course, there is an exception to this rule, namely when we talk about nested or inner classes, as these - nested or inner classes - have an implied qualifier reference to the outer class object, along with it’s methods and attributes.

This will become more understandable with the following code snippet:

class HelloJava {
    public static void main(String[] args) {
        int i = 10;
        class MyInnerClass {
            {
                int i = 20;
                System.out.println( "the value of i: " + i);
            }
        }
        new MyInnerClass();
    }
}

The output of the above program is:

the value of i: 20

So remember, the inner class has an implied reference to it’s parent’s methods and attributes, thus can create shadowed variables.