User Professor Hans Noodles
Professor Hans Noodles
Level 41

Examples of inheritance of nested classes

Published in the Java Developer group
Hi! Today we'll look at an important mechanism: inheritance in nested classes. Examples of inheritance of nested classes - 1 Have you ever thought about what you would do if you needed to make a nested class inherit some other class. If not, believe me: this situation can be confusing, because there are a lot of nuances.
  1. Are we making a nested class inherit some class? Or are we making some class inherit a nested class?
  2. Is the child/parent class an ordinary public class, or is it also a nested class?
  3. Finally, what type of nested classes do we use in all these situations?
There are so many possible answers to all these questions, your head will spin :) As you know, we can solve a complex problem by dividing it into simpler parts. Let's do that. Let us consider each group of nested classes in turn from two perspectives: who can inherit each type of nested class, and who it can inherit. Let's start with static nested classes.

Static nested classes

Examples of inheritance of nested classes - 2Their inheritance rules are the simplest. Here you can do almost anything your heart desires. A static nested class can inherit:
  • an ordinary class
  • a static nested class that is declared in an outer class or its ancestors
Recall an example from our lesson on static nested classes.

public class Boeing737 {

   private int manufactureYear;
   private static int maxPassengersCount = 300;

   public Boeing737(int manufactureYear) {
       this.manufactureYear = manufactureYear;
   }

   public int getManufactureYear() {
       return manufactureYear;
   }

   public static class Drawing {
      
       public static int getMaxPassengersCount() {
          
           return maxPassengersCount;
       }
   }
}
Let's try to change the code and create a Drawing static nested class and its descendant — Boeing737Drawing.

public class Boeing737 {

   private int manufactureYear;
   private static int maxPassengersCount = 300;

   public Boeing737(int manufactureYear) {
       this.manufactureYear = manufactureYear;
   }

   public int getManufactureYear() {
       return manufactureYear;
   }

   public static class Drawing {
      
   }
  
   public static class Boeing737Drawing extends Drawing {

       public static int getMaxPassengersCount() {

           return maxPassengersCount;
       }
   }
}
As you can see, no problem. We can even pull out the Drawing class and make it an ordinary public class instead of a static nested class — nothing will change.

public class Drawing {
  
}

public class Boeing737 {

   private int manufactureYear;
   private static int maxPassengersCount = 300;

   public Boeing737(int manufactureYear) {
       this.manufactureYear = manufactureYear;
   }

   public int getManufactureYear() {
       return manufactureYear;
   }

   public static class Boeing737Drawing extends Drawing {

       public static int getMaxPassengersCount() {

           return maxPassengersCount;
       }
   }
}
We understand this. But what classes can inherit a static nested class? Practically any! Nested/non-nested, static/non-static — it doesn’t matter. Here we make the Boeing737Drawing inner class inherit the Drawing static nested class:

public class Boeing737 {

   private int manufactureYear;
   private static int maxPassengersCount = 300;

   public Boeing737(int manufactureYear) {
       this.manufactureYear = manufactureYear;
   }

   public int getManufactureYear() {
       return manufactureYear;
   }

   public static class Drawing {
      
   }

   public class Boeing737Drawing extends Drawing {

       public int getMaxPassengersCount() {

           return maxPassengersCount;
       }
   }
}
You can create an instance of Boeing737Drawing like this:

public class Main {

   public static void main(String[] args) {

      Boeing737 boeing737 = new Boeing737(1990);
      Boeing737.Boeing737Drawing drawing = boeing737.new Boeing737Drawing();
      System.out.println(drawing.getMaxPassengersCount());

   }

}
Although our Boeing737Drawing class inherits a static class, it is not static itself! As a result, it will always need an instance of the outer class. We can remove the Boeing737Drawing class from the Boeing737 class and make it a simple public class. Nothing changes. It can still inherit the Drawing static nested class.

public class Boeing737 {

   private int manufactureYear;
   public static int maxPassengersCount = 300;

   public Boeing737(int manufactureYear) {
       this.manufactureYear = manufactureYear;
   }

   public int getManufactureYear() {
       return manufactureYear;
   }

   public static class Drawing {

   }
}

public class Boeing737Drawing extends Boeing737.Drawing {

   public int getMaxPassengersCount() {

       return Boeing737.maxPassengersCount;
   
}
The only important point is that in this case we need to make the static maxPassengersCount variable public. If it remains private, then an ordinary public class won't have access to it. We've figured out static classes! :) Now let's move on to inner classes. They come in 3 types: simple inner classes, local classes, and anonymous inner classes. Examples of inheritance of nested classes - 3Again, let's move from simple to complex :)

Anonymous inner classes

An anonymous inner class cannot inherit another class. No other class can inherit an anonymous class. It couldn't be any simpler! :)

Local classes

Local classes (in case you forgot) are declared inside a code block of another class. Most often, this happens inside some method of the outer class. Logically, only other local classes inside the same method (or code block) can inherit a local class. Here is an example:

public class PhoneNumberValidator {

   public void validatePhoneNumber(final String number) {

       class PhoneNumber {

           private String phoneNumber;

           public PhoneNumber() {
               this.phoneNumber = number;
           }

           public String getPhoneNumber() {
               return phoneNumber;
           }

           public void setPhoneNumber(String phoneNumber) {
               this.phoneNumber = phoneNumber;
           }
       }

       class CellPhoneNumber extends PhoneNumber {

       }

       class LandlinePhoneNumber extends PhoneNumber {
          
          
       }

       // ...number validation code
   }
}
This is the code from our lesson on local classes. Our number validator class has a PhoneNumber local class. If we need it to represent two distinct entities, for example, a mobile phone number and a landline phone number, we can only do this inside the same method. The reason is simple: a local class's scope is limited to the method (code block) where it is declared. As a result, we won't be able to use it externally (including for class inheritance). However, the possibilities for inheritance within the local class itself are much wider! A local class can inherit:
  1. An ordinary class.
  2. An inner class that is declared in the same class as the local class or in one of its ancestors.
  3. Another local class declared in the same method (code block).
The first and third points look obvious, but the second one is a bit confusing :/ Let's look at two examples. Example 1 — "Making a local class inherit an inner class declared in the same class as the local class":

public class PhoneNumberValidator {

   class PhoneNumber {

       private String phoneNumber;

       public PhoneNumber(String phoneNumber) {
           this.phoneNumber = phoneNumber;
       }

       public String getPhoneNumber() {
           return phoneNumber;
       }

       public void setPhoneNumber(String phoneNumber) {
           this.phoneNumber = phoneNumber;
       }
   }

   public void validatePhoneNumber(final String number) {

       class CellPhoneNumber extends PhoneNumber {

           public CellPhoneNumber(String phoneNumber) {
               super(number);
           }
       }

       class LandlinePhoneNumber extends PhoneNumber {

           public LandlinePhoneNumber(String phoneNumber) {
               super(number);
           }
       }

       // ...number validation code
   }
}
Here we removed the PhoneNumber class from the validatePhoneNumber() method and made it an inner class instead of a local class. This doesn't stop us from making our 2 local classes inherit it. Example 2 — "... or in the ancestors of this class." Now this is already more interesting. We can move PhoneNumber even higher in the inheritance chain. Let's declare an abstract AbstractPhoneNumberValidator class, which will become the ancestor of our PhoneNumberValidator class:

public abstract class AbstractPhoneNumberValidator {

   class PhoneNumber {

       private String phoneNumber;

       public PhoneNumber(String phoneNumber) {
           this.phoneNumber = phoneNumber;
       }

       public String getPhoneNumber() {
           return phoneNumber;
       }

       public void setPhoneNumber(String phoneNumber) {
           this.phoneNumber = phoneNumber;
       }
   }

}
As you can see, we didn't just declare it — we also moved the PhoneNumber inner class into it. However, in its descendant PhoneNumberValidator, local classes declared in methods can inherit PhoneNumber without any problem!

public class PhoneNumberValidator extends AbstractPhoneNumberValidator {

   public void validatePhoneNumber(final String number) {

       class CellPhoneNumber extends PhoneNumber {

           public CellPhoneNumber(String phoneNumber) {
               super(number);
           }
       }

       class LandlinePhoneNumber extends PhoneNumber {

           public LandlinePhoneNumber(String phoneNumber) {
               super(number);
           }
       }

       // ...number validation code
   }
}
Due to the inheritance relationship, the local classes inside a descendant class "see" the inner classes inside an ancestor. And finally, let's proceed to the last group :)

Inner classes

A inner class declared in the same outer class (or in its descendant) can inherit another inner class. Let's explore this using our example with bicycles from the lesson on inner classes.

public class Bicycle {

   private String model;
   private int maxWeight;

   public Bicycle(String model, int maxWeight) {
       this.model = model;
       this.maxWeight = maxWeight;
   }

   public void start() {
       System.out.println("Let's go!");
   }

   class Seat {

       public void up() {

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

       public void down() {

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

   class SportSeat extends Seat {
      
       // ...methods
   }
}
Here we declared the Seat inner class inside the Bicycle class. A special type of racing seat, SportSeat, inherits it. But, we could create a separate "racing bicycle" type and put it in a separate class:

public class SportBicycle extends Bicycle {
  
   public SportBicycle(String model, int maxWeight) {
       super(model, maxWeight);
   }

  
   class SportSeat extends Seat {

       public void up() {

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

       public void down() {

           System.out.println("Seat down!");
       }
   }
}
This is also an option. The inner class of the descendant (SportBicycle.SportSeat) "sees" the inner classes of the ancestor and can inherit them. Inheriting inner classes has one very important feature! In the previous two examples, our SportSeat class was an inner class. But what if we decide to make SportSeat an ordinary public class that simultaneously inherits the Seat inner class?

// Error! No enclosing instance of type 'Bicycle' is in scope
class SportSeat extends Bicycle.Seat {

   public SportSeat() {

   }

   public void up() {

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

   public void down() {

       System.out.println("Seat down!");
   }
}
We got an error! Can you guess why? :) It's all straightforward. When we talked about the Bicycle.Seat inner class, we mentioned that a reference to an instance of the outer class is implicitly passed to the constructor of the inner class. This means that you can't create a Seat object without creating a Bicycle object. But what about the creation of a SportSeat? Unlike Seat, it does not have this built-in mechanism for implicitly passing the constructor a reference to an instance of the outer class. S till, without a Bicycle object, we cannot create a SportSeat object, just as in the case of Seat. Therefore, there's only one thing left for us to do — explicitly pass to the SportSeat constructor a reference to a Bicycle object. Here's how to do it:

class SportSeat extends Bicycle.Seat {

   public SportSeat(Bicycle bicycle) {

       bicycle.super();
   }

   public void up() {

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

   public void down() {

       System.out.println("Seat down!");
   }
}
We call the superclass constructor using super(); Now, if we want to create a SportSeat object, nothing will stop us from doing this:

public class Main {

   public static void main(String[] args) {

       Bicycle bicycle = new Bicycle("Peugeot", 120);
       SportSeat peugeotSportSeat = new SportSeat(bicycle);

   }
}
Phew! This lesson was rather long :) But you learned a lot! Examples of inheritance of nested classes - 4Now it's time to solve some tasks! :)
Comments (4)
TO VIEW ALL COMMENTS OR TO MAKE A COMMENT,
GO TO FULL VERSION
Chrisantus Makokha Level 30, Nairobi, Kenya
21 May 2021
To be revisited from time to time.
Andrei Level 41
12 April 2021
Man, this was crazy.. Inner classes are so convoluted, like a maze! I wonder if you can hide like viruses or something in them to make them undetectable? lol
null Level 26, Orlando, United States
13 August 2020
What about the case the inner class inherit nested static class? is it allowed? since nested static has no ref to its outerclass