DEV Community

Cover image for Introduction to nested classes in Java
Yuri Mednikov
Yuri Mednikov

Posted on

Introduction to nested classes in Java

Hi! This post is dedicated to an important concept from Java object oriented programming - nested classes. Speaking simply, nested class is a class, defined inside an another one (outer class). They serve to provide a better level of encapsulation and to organize logically code, that is used in one place only. You also meet nested classes when you work with GUI development (for instance, in Android programming). Finally, if you are serious about passing Oracle exams, you have to master nested classes as tasks with them present on both stages.

In the article we will observe what is a nested class in Java and concrete types of such classes.

What is a nested class?

From a technical point of view, a nested class is a class, that is defined inside the another class. An outer class is also called an enclosing class. These classes enable developers to logically group classes that are only used in one place, and therefore this will increase the degree of encapsulation, and will create more readable and maintainable code. Nested classes in Java are divided into two categories: static and non-static. First group are also called static nested classes. Non-static nested classes are known as inner classes. Take a look on the graph below, that demonstrates an hierarchy of Java nested classes:

There are several arguments for using nested classes:

  1. When a class is useful to only one other class, it is logical to embed it in that class and keep the two together
  2. Nesting small classes within top-level classes places the code closer to where it is used
  3. An usage of nested classes is strong measure to increase a level of encapsulation

These types of nested classes differ on a basis of scope, accessing of enclosing class members etc. Now, we will investigate each of them more deeper. We will start with static nested classes.

Static classes

First group to talk is static nested classes group.

Static nested classes

The static nested class is associated with its enclosing class. It cannot refer directly to instance variables or methods defined in its enclosing class - in other words you have to use an object reference. Let have a look on the following example:

class Outer{

    static class Nested{

    }
}

// create nested class instance
Outer.Nested nestedInstance = new Outer.Nested();
Enter fullscreen mode Exit fullscreen mode

Note, that in the example code, the static nested class are accessed using the outer class name. The nested class cannot access non-static data members and methods of outer class can access static data members of outer class including those that are declared as private. Take a look on the another code snippet:

class Car{

//static members
    static String manufacturer;
    private static String vin;

// instance member
    String model;


    static class Engine{
        System.out.println(manufacturer);
        System.out.println(vin);

    //This will be a compilation error
        System.out.println(model);
    }
}
Enter fullscreen mode Exit fullscreen mode

So, what should remember about static nested classes? Following points:

  • Static nested classes can access static members of the outer class
  • They can't access instance (non-static) members of the outer class directly, only using object reference (like Outer.Nested)
  • Static nested classes are created as OuterClass.StaticNestedClass nestedObject = new OuterClass.StaticNestedClass();

Opposite, there are several types of non-static inner classes.

Non-static classes

Non-static classes are associated with an instance of the enclosing class and have direct access to the enclosing class's methods and fields. Also, because an inner class is associated with an instance of the outer class, it cannot define any static members itself. Let observe types of non-static classes.

Local inner classes

Local inner classes are classes that are defined in a block, which is a group of zero or more statements between balanced braces. Generally, these classes are defined within a method, however you can define a local class inside any block. Let observe on the code below:

class Outer{

    int value = 10;

    void calculate(){

        class Sum{

            int value;

            Sum(int a, int b){
                this.value = a + b;
            }

            int getValue(){
                return this.value;
            }
        }

        Sum sum = new Sum(20,30);
        System.out.println(sum.getValue());
        System.out.println(value);
    }

    public static void main(String[] args) {
        Outer outer = new Outer();
        outer.calculate();
    }
}
Enter fullscreen mode Exit fullscreen mode

So, this example demonstates the concept of shadowing: if a declaration of a type (such as a member variable or a parameter name) in a particular scope (such as an inner class or a method definition) has the same name as another declaration in the enclosing scope, then the declaration shadows the declaration of the enclosing scope. In the example we have two value variables and to refer to Sum's member we used this keyword. Declarations of a type (such as a variable) in a local class shadow declarations in the enclosing scope that have the same name.

Talking about local inner class it is also important to talk about accessing local variables. Let modify the code above:

void calculate(){

    int y - 10;

    class Multiplicator{

        int x;

        Sum(int x){
            this.value = x * y;
        }

        int getValue(){
            return this.value;
        }
    }

    Multiplicator multiplicator = new Multiplicator(10);
    System.out.println(multiplicator.getValue());
}
Enter fullscreen mode Exit fullscreen mode

How can Multiplicator class access a local variable y? This is a popular interview question. Local classes can only access local variables that are declared final. When a local class accesses a local variable or parameter of the enclosing block, it captures that variable or parameter. But that how it was before Java 8. However, in the 8th release was introduced a concept of effectively final varialbes: these are variables or parameters whose value is never changed after it is initialized.

While, effectively final variables do not need to have final keyword in front of them, anyway they can't be changed after initialization. Following this principle, this code snipper will not compile:

void calculate(){

    int y - 10;

    class Multiplicator{

        int x;

        Sum(int x){
            this.value = x * y;
        }

        int getValue(){
            return this.value;
        }
    }

    y = 20;

    Multiplicator multiplicator = new Multiplicator(10);
    System.out.println(multiplicator.getValue());
}
Enter fullscreen mode Exit fullscreen mode

When we will try to compile it, Java will indicate an error output similar to this one:

Let summarize what we learned so far about local inner classes:

  • Local inner class scope is the block where it is defined
  • It can't be instantiated from outside the block where it is created
  • It can access local variables if they are final (prior to Java 8) or effectively final
  • Local inner class has access to the members of its enclosing class

Also, local inner classes can extend an abstract class or can also implement an interface.

Anonymous classes

Finally let explore anonymous classes. From a technical point of view, they are like local inner classes except that they do not have a name. So we use them only if we require to overload methods of a class or interface, without having to actually extend (implement) it. Let go directly to an example:

void doWork(){
    Runnable runnable = new Runnable() {
        @Override
        public void run() {
            System.out.println("Hello runnable");
        }
    };
    runnable.run();
}
Enter fullscreen mode Exit fullscreen mode

We implement a Runnable interface directly inside a method that calls it. This enables to make code more concise. We can to declare and instantiate a class at the same time. You can declare inside an anonymous class fields and extra methods that do not exist inside parent class or interface or even add local classes. However, you cannot declare constructors in an anonymous class.

What is important to remember about anonymous classes:

  • They have access to the members of its enclosing class
  • Same as local inner classes, they can't access local variables in its enclosing scope that are not declared as final or effectively final.
  • Like a nested class, a declaration of a type (such as a variable) in an anonymous class shadows any other declarations in the enclosing scope that have the same name
  • An anonymous class can have static members provided that they are constant variables

Conclusion

In this tutorial we explored different types of nested classes in Java. This topic is very important, so if you are serious about passing Oracle exams, you have to master nested classes as tasks with them present on both stages. We discussed how to use and what are restrictions for each of them.

References

  • J Steven Perry Unit 18: Nested classes (2016) IBM Developer, read here
  • Manoj Debnath Understanding Java Nested Classes and Java Inner Classes (2017) Developer.com, read here

Top comments (0)