Variable scoping
Each variable is only available (and accessible) in the context within which it has been declared. To determine the context, refer to the closest opening and closing marks that surround the declaration. Most programming languages use curly braces ({}
) to mark the beginning and the end of a block of code. Check out this example:
There's no need to understand every bit of code here; just focus on the curly braces. 🙂 When we talk about the availability of a variable within a context, we're referring to scope. Here, you can see that the variable root
has been declared between the two curly braces circled in purple. The scope of that variable is everything between those two braces.
The scope of a variable can be local as well as global, depending on where a variable is declared. A global variable can be available to all the classes and methods within a program while a local variable might only be available within the method that it is declared in:
Here, we've expanded the code a little and extended our first block of code to include another one! If you trace the purple lines, you can see that the brackets from the first block encompass all the code from the second one. Next, you can see that a new variable, spy
, has been declared within the green local scope.
Since the root
variable has been declared in the global scope, that means it is accessible to everything within the purple brackets, including everything declared in the local scope. In the second block of code, you can see a line just below the spy variable declaration which uses root
. That's allowed!
However, whatever is in the local scope is not available to the global scope, or any other local blocks of code. Let's take a look at another example:
Here we've added another block of code, which has its own local scope and its own variable, anotherSpy
. Now, check out the last line in our spy variable block:
System.out.println(anotherSpy); // Error
Looks like there's an error! That's because it's trying to use the anotherSpy
variable. But it can't because anotherSpy
is neither within the global scope nor in the same local scope. That means that this block of code can't access it. anotherSpy
is only available in the block of code it's been declared in.
The same is true in the other direction. You can see that the last line of our last block of code also has an error:
System.out.println(spy); // Error
Here, it's trying to use the variable spy from another block of code. But it can't since spy
isn't in the same scope as the code block that's trying to use it.
Variable scope in classes
When you declare a class, the same general rules of scoping apply: each variable is only accessible within its declaration block. Let's experiment with a unicorn class:
Just like in our first example, there are global class variables as well as local ones. Let's review in detail:
The variables
height
andpower
are fields of the class and are accessible anywhere within the class.The variable
minutesToSleep
is only accessible within the local scope of the block of code it's declared in.The variable
minutesToRun
is only accessible within the local scope of the block of code it's declared in.
The scope of a variable limits its accessibility by definition. However, class fields are accessible outside of the class and can be used by any other block of code.
In our example, those are height
and power
. If we declare a Unicorn variable, we can read or alter those values:
Unicorn firefly = new Unicorn();
System.out.println("I know it's height: "+unicorn.height);
// and can change its power!
unicorn.power = 0; // no fun!
Being able to mess around with class variables can have serious consequences. The good news is - you can control that! Before we check out how, be sure to practice with variable scoping.
Access control
We are going use the idea of access control by implementing restricted access to a class, module, or file. You already know what a class and a file are since we’ve been working with them!
A module is a bundle of related source files associated with a name, like a framework. A framework is a set of functionalities grouped by a particular context. It speeds up the development process and provides guidance on how to write the code.
There are a number of frameworks provided by every development environment. The fact is that the implementation of those frameworks is far beyond what the developers who use them can see and utilize. That’s done by restricting access to the details of implementation, also known as implementing access control.
Control levels
In Java, you need to use one of the keywords to designate a control level:
Public: visible to the world and therefore the least restrictive
Protected: visible to the package and all its subclasses
Package-protected: typically visible only to the package they are in (default settings)
Private: only accessible in the context in which they are defined (inside the class it is located)
Putting those distinct restrictions in place makes the development a lot easier. You don't have to worry about any unwanted visibility of your implementation and more so, unwanted modifications.
Place a suitable keyword in front of the related declaration:
class Unicorn {
// properties
private int height = 170;
public String power = "Double.infinity";
// methods
private static void sleep() {
}
public static void run() {
}
}
Then, if you try to access private members from outside of the class, you get errors:
Unicorn unicorn = new Unicorn();
System.out.println(unicorn.power); // Ok
unicorn.height = 180; // Error
unicorn.sleep(); // Error
unicorn.run(); // Ok
The control levels can be assigned to class elements as well as classes:
public class PublicClass {
}
private class PrivateClass {
}
In addition to security, specifying control levels for class members provides better readability. When a developer is readying a source file, it's always clear which elements can be used externally.
Hierarchy of control
An element may have the same or more restrictive control level than its containing element:
public class PublicClass {
public boolean publicProperty = true;
int internalProperty = 0; //default at package-private
private String fileprivateProperty = "Hello!"
private static void privateMethod() {
}
}
In the example above, the class is declared as public
. Since the class is the containing element, this means that all elements of that class may be of the same or lesser level of exposure. In this case, it includes all levels.
If you declare a class as private
, its elements can only be package-private
or private
:
class PrivateClass {
int internalProperty = 0; // automatically assigned package-private by default
protected defaultProperty = true; // automatically assigned package-private
public boolean publicProperty = true; // automatically converted to package-private
private String fileprivateProperty = "Hello!"; //only available to the class
private static void privateMethod() {
}
}
In the example above, we've added an attribute without an explicit access control keyword. In this scenario, by default, it assumes the level of the containing element. In this case, it's our class, so it assumes the level of PrivateClass
.
A top-level class cannot be marked private, but setting it as default will put it in the package-protected level. When declaring a variable of a class, if the control level of the declaration context is higher than the one of the class, the variable must also have an explicit control level. Let's declare a FileprivateClass variable:
String a = PrivateClass(); // Error
private String b = PrivateClass(); // Ok
private String c = PrivateClass(); // Ok
As you can see, if the default access level of variable's context is higher than a class you are assigning to it, you must explicitly specify the variable's level as the same or lower than the one of the class.
Summary
In this chapter, you've learned about variable accessibility:
A scope of a variable is the code area where it's been declared.
General variable scope applies to all code blocks, including classes.
Another necessary way to control access to variables and functions is using control levels: public, protected, package-protected, and private.