User Professor Hans Noodles
Professor Hans Noodles
Level 41

Nested inner classes

Published in the Java Developer group
Hi! Today we'll take up an important topic: how nested classes work in Java. Nested inner classes - 1 Java lets you create classes inside another class:

class OuterClass {
    ...
    static class StaticNestedClass {
        ...
    }
    class InnerClass {
        ...
    }
}
These internal classes are called nested. They are divided into 2 types:
  1. Non-static nested classes. These are also called inner classes.
  2. Static nested classes.
In turn, inner classes have two distinct subcategories. In addition to an inner class simply being an inner class, it can also be:
  • a local class
  • an anonymous class
Confused? :) That's okay. Here's a diagram for clarity. Come back to it during the lesson if you suddenly find yourself confused! Nested inner classes - 2In today's lesson, we'll discuss inner classes (also known as non-static nested classes). They are specially highlighted in the overall diagram so you don't get lost :) Let's start with the obvious question: why are they called "inner" classes? The answer is quite simple: because they are created inside other classes. Here is an example:

public class Bicycle {

   private String model;
   private int weight;

   public Bicycle(String model, int weight) {
       this.model = model;
       this.weight = weight;
   }
  
   public void start() {
       System.out.println("Let's go!");
   }

   public class Handlebar {

       public void right() {
           System.out.println("Steer right!");
       }

       public void left() {

           System.out.println("Steer left!");
       }
   }

   public class Seat {
      
       public void up() {

           System.out.println("Seat up!");
       }
      
       public void down() {

           System.out.println("Seat down!");
       }
   }
}
Here we have the Bicycle class. It has 2 fields and 1 method: start(). Nested inner classes - 3It differs from an ordinary class in that it contains two classes: Handlebar and Seat. Their code is written inside the Bicycle class. These are full-fledged classes: as you can see, each of them has its own methods. At this point, you might have a question: why in the world would we put one class inside another? Why make them inner classes? Well, suppose we need separate classes for the concepts of handlebar and seat in our program. Of course, it's not necessary for us to make them nested! We can make ordinary classes. For example, like this:

public class Handlebar {
   public void right() {
       System.out.println("Steer right!");
   }

   public void left() {

       System.out.println("Steer left");
   }
}

public class Seat {

   public void up() {

       System.out.println("Seat up!");
   }

   public void down() {

       System.out.println("Seat down!");
   }
}
Very good question! Of course, we aren't limited by the technology. Doing that is certainly an option. Here, the important thing is more the correct design of the classes from the perspective of a specific program and its purpose. Inner classes are for separating out an entity that is inextricably connected to another entity. Handlebars, seats, and pedals are components of a bicycle. Separated from the bicycle, they don't make much sense. If we made all these concepts separate public classes, we would have had the code like this in our program:

public class Main {

   public static void main(String[] args) {
       Handlebar handlebar = new Handlebar();
       handlebar.right();
   }
}
Hmm... The meaning of this code is even difficult to explain. We have some vague handlebar (Why is it necessary? No idea, to be honest). And this handle turns right... all by itself, without a bicycle... for some reason. By separating the concept of the handlebar from the concept of the bicycle, we lost some logic in our program. Using an inner class, the code looks very different:

public class Main {

   public static void main(String[] args) {

       Bicycle peugeot = new Bicycle("Peugeot", 120);
       Bicycle.Handlebar handlebar = peugeot.new Handlebar();
       Bicycle.Seat seat = peugeot.new Seat();

       seat.up();
       peugeot.start();
       handlebar.left();
       handlebar.right();
   }
}
Console output:

Seat up! 
Let's go! 
Steer left! 
Steer right!
Now what we see suddenly makes sense! :) We created a bicycle object. We created two bicycle "subobjects" — a handlebar and a seat. We raised the seat for comfort and off we went: pedaling and steering as needed! :) The methods we need are called on the appropriate objects. It's all simple and convenient. In this example, separating out the handlebar and seat enhances encapsulation (we hide data about the bicycle parts inside the relevant class) and lets us create a more detailed abstraction. Now let's look at a different situation. Suppose we want to create a program that simulates a bike shop and spare parts for bikes. Nested inner classes - 4In this situation, our previous solution won't be work. At a bike store, each individual bicycle part makes sense even when separated from a bicycle. For example, we'll need methods like "sell pedals to a customer", "buy a new seat", etc. It would be a mistake to use inner classes here — each individual bicycle part in our new program has meaning that stands on its own: it can be separated from the concept of a bicycle. This is precisely what you need to pay attention to if you're wondering whether you should use inner classes or organize all the entities as separate classes. Object-oriented programming is good in that it makes it easy to model real-world entities. This can be your guiding principle when deciding whether to use inner classes. In a real store, spare parts are separate from bicycles — this is okay. This means that it is also okay when designing a program. Okay, we've figured out the "philosophy" :) Now let's get acquainted with important "technical" features of inner classes. Here's what you definitely need to remember and understand:
  1. An object of an inner class cannot exist without an object of an outer class.

    This makes sense: this is why we made the Seat and Handlebar inner classes in our program — so that we don't end up with orphaned handlebars and seats.

    This code does not compile:

    
    public static void main(String[] args) {
    
       Handlebar handlebar = new Handlebar();
    }
    

    Another important feature follows from this:

  2. An object of an inner class has access to the variables of the outer class.

    For example, let's add an int seatPostDiameter variable (representing the diameter of the seatpost) to our Bicycle class.

    Then in the Seat inner class, we can create a displaySeatProperties() method that displays the seat properties:

    
    public class Bicycle {
    
       private String model;
       private int weight;
    
       private int seatPostDiameter;
    
       public Bicycle(String model, int weight, int seatPostDiameter) {
           this.model = model;
           this.weight = weight;
           this.seatPostDiameter = seatPostDiameter;
    
       }
    
       public void start() {
           System.out.println("Let's go!");
       }
    
       public class Seat {
    
           public void up() {
    
               System.out.println("Seat up!");
           }
    
           public void down() {
    
               System.out.println("Seat down!");
           }
    
           public void displaySeatProperties() {
    
               System.out.println("Seat properties: seatpost diameter = " + Bicycle.this.seatPostDiameter);
           }
       }
    }
    

    And now we can display this information in our program:

    
    public class Main {
    
       public static void main(String[] args) {
    
           Bicycle bicycle = new Bicycle("Peugeot", 120, 40);
           Bicycle.Seat seat = bicycle.new Seat();
    
           seat.displaySeatProperties();
       }
    }
    

    Console output:

    
    Seat properties: seatpost diameter = 40
    

    Note: the new variable is declared with the most strict access modifier (private). And still the inner class has access!

  3. An object of an inner class cannot be created in a static method of an outer class.

    This is explained by the specific features of how inner classes are organized. An inner class can have constructors with parameters, or just the default constructor. But regardless, when we create an object of an inner class, a reference to the object of the outer class is invisibly passed to the created object of the inner class. After all, the presence of such an object reference is an absolute requirement. Otherwise, we won't be able to create objects of the inner class.

    But if a method of the outer class is static, then we might not have an object of the outer class! And this would be a violation of the logic of how an inner class works. In this situation, the compiler will generate an error:

    
    public static Seat createSeat() {
      
       // Bicycle.this cannot be referenced from a static context
       return new Seat();
    }
    
  4. An inner class cannot contain static variables and methods.

    The logic is the same: static methods and variables can exist and be called or referenced even in the absence of an object.

    But without an object of the outer class, we won't have access to the inner class.

    A clear contradiction! This is why static variables and methods are not allowed in inner classes.

    The compiler will generate an error if you try to create them:

    
    public class Bicycle {
    
       private int weight;
    
    
       public class Seat {
          
           // An inner class cannot have static declarations
           public static void displaySeatProperties() {
    
               System.out.println("Seat properties: seatpost diameter = " + Bicycle.this.seatPostDiameter);
           }
       }
    }
    
  5. When creating an object of an inner class, its access modifier is important.

    An inner class can be marked with the standard access modifiers: public, private, protected, and package private.

    Why does this matter?

    This affects where we can create instances of the inner class in our program.

    If our Seat class is declared as public, we can create Seat objects in any other class. The only requirement is that an object of the outer class must also exist.

    By the way, we already did this here:

    
    public class Main {
    
       public static void main(String[] args) {
    
           Bicycle peugeot = new Bicycle("Peugeot", 120);
           Bicycle.Handlebar handlebar = peugeot.new Handlebar();
           Bicycle.Seat seat = peugeot.new Seat();
    
           seat.up();
           peugeot.start();
           handlebar.left();
           handlebar.right();
       }
    }
    

    We easily gained access to the Handlebar inner class from the Main class.

    If we declare the inner class as private, we will be able to create objects only inside the outer class.

    We can no longer create a Seat object "on the outside":

    
    private class Seat {
    
       // Methods
    }
    
    public class Main {
    
       public static void main(String[] args) {
    
           Bicycle bicycle = new Bicycle("Peugeot", 120, 40);
    
           // Bicycle.Seat has private access in Bicycle
           Bicycle.Seat seat = bicycle.new Seat();
       }
    }
    

    You probably already understand the logic :)

  6. Access modifiers for inner classes work the same as for ordinary variables.

    The protected modifier provides access to an instance variable in subclasses and classes that are in the same package.

    protected also works for inner classes. We can create protected objects of the inner class:

    • in the outer class;
    • in its subclasses;
    • in classes that are in the same package.

    If the inner class does not have an access modifier (package private), objects of the inner class can be created:

    • in the outer class;
    • in classes that are in the same package.

    You have been familiar with modifiers for a long time, so no problems here.

Nested inner classes - 5That's all for now :) But don't slack off! Inner classes are a fairly extensive topic that we'll continue to explore in the next lesson. Now you can refresh your memory of our course's lesson on inner classes. And next time, let's talk about static nested classes.
Comments (2)
TO VIEW ALL COMMENTS OR TO MAKE A COMMENT,
GO TO FULL VERSION
Andrei Level 41
6 April 2021
I don't understand the difference between the last picture and the picture before that. Besides that in the last picture, the Seat class is outside Main class, I don't see any other difference and don't really understand the difference between points 5 and 6.