Essence of Variable Scope in Java 8
In Java, a class can contain several types of members; variables, methods or even other classes. Having other classes as members inside a particular class is often referred to as “nesting classes”. Here, the class written within is called the nested class, and the class that holds the nested class is called the outer class. Java SE 8 brings about some changes to the variable scope related to nesting of classes.
Let’s take a look at types of nested classes:
Basically, there are two types of nested classes:
- Static nested classes
- Non static nested classes (also known as inner classes)
The distinction between the two types is simple. Static nested classes are static members of a class. As the name itself suggests, non static nested classes are non static members of a class.
Note: According to the above classification, it is obvious that you simply cannot refer to any sort of nested class as an inner class, because inner classes are a special type of nested classes.
In this post I will be talking about the changes in the variable scope brought about in Java SE 8 related to non static nested classes which are also known as inner classes.
Types of inner classes:
There are two types of inner classes:
- Anonymous inner classes
- Local inner classes
An anonymous class is an inner class which is declared without using a name at all. The reason why they are called “anonymous” classes is because they are just like local classes but have no names. An anonymous class definition has some unusual syntax. It is similar to the syntax of a constructor invocation except that there is a class definition inside a block of code.
Anonymous inner class example:
In the code below, inside the class OuterClass, there is an interface definition called Hello.
The highlighted code segment includes the creation of an instance of a sub class implementing the interface Hello, which is only a reference variable. Let me explain this further. Notice that this section somewhat looks like creation of a normal class instance. But, there IS a sub class definition inside the highlighted code. However, there is no specific name given to the sub class out of which the instance anonymousInnerClassInstance is created. That is why a block of code like this is called an anonymous inner class.
public class OuterClass {private String message = “Hello!”;interface Hello {
public void greet();
}public Hello anonymousInnerClassInstance = new Hello() {
@Override
public void greet() {System.out.println(message + “ Have a great day!”);
}
};}public class Main {
public static void main(String[] args) {OuterClass demoOuterClass = new OuterClass();
demoOuterClass.anonymousInnerClassInstance.greet();
}
}
Output
Hello! Have a great day!
A local class on the other hand is defined within a block. A block has zero or more statements enclosed between balanced curly braces. Therefore, local classes can be defined inside methods, for loops, if clauses etc. I will be using an example of a local class to discuss nitty gritties of variable scope later in this post.
Now that we are clear on types of nested classes, let’s discuss some points related to variable scope before and after Java 8.
Example supported before Java 8:
Let’s say we want to display a hello message for a given name.
The class LocalClassExampleBeforeJava8 has a static method called displayMessage which has a parameter name1. Before Java 8, the local variables of the enclosing scope had to be declared as final. Hence, the local class Greeting has a final local variable called text. It’s constructor takes the value of the variable text and appends a string name to form the formattedMessage. Before Java 8, a local class could not capture the value of a parameter name1 directly in its methods. Therefore, an instance of Greeting class had to be created which is outside the scope of the local class and then capture the value of the parameter name1. See line number 27.
public class LocalClassExampleBeforeJava8 {public static void displayMessage(String name1) {//final local variable
final String text = “Hello”;//local class
class Greeting {private String formattedMessage = null;Greeting(String name) {formattedMessage = text + “ “ + name;
}public String getFormattedMessage() {
return formattedMessage;
}}Greeting greeting = new Greeting(name1);
System.out.println(greeting.getFormattedMessage());
}public static void main(String[] args) {displayMessage(“Loch”);
}
}
Output
Hello Loch
Example supported after Java 8:
The below code carries out the same functionality of displaying a hello message for a given name but includes some features only supported by JDK 8. Notice that local variable text is no longer final as highlighted in line number 9.
Before Java 8, inner classes were only allowed to access final local variables. This rule has now been relaxed. Now, an inner class may also access any effectively final local variable — that is, any variable whose value does not change. In the example I have given the variable text is effectively final. The value of text has not been changed inside the local class Greeting.
Another difference with respect to earlier code is shown in line number 17. Here, I have been able to access the parameter name1 with in a method in the local class; printFormattedMessage( ). In Java 8, we can capture values of free variables as long as they are not mutated with in the code.
public class LocalClassExampleWithJava8 {
public static void displayMessage(String name1) {//effectively final local variable
String text = “Hello”;//local class
class Greeting {private String formattedMessage = null;public void PrintFormattedMessage() {
formattedMessage = text + “ “ + name1;
System.out.println(formattedMessage);
}}Greeting greeting = new Greeting();
greeting.PrintFormattedMessage();}public static void main(String[] args) {displayMessage(“Loch”);
}
}
Output
Hello Loch