JavaRush /Java Blog /Random EN /Inheritance of inner classes (nested classes)

Inheritance of inner classes (nested classes)

Published in the Random EN group
Hello! Today we will look at the operation of an important mechanism - inheritance in nested classes. I don't know if you've ever thought about what you'll do when you need to inherit a nested class from some other. If not, believe me: such a situation can be confusing, because there are a lot of nuances here:
  1. Do we inherit a nested class from some class or do we inherit another class from a nested one?
  2. Is the inheritor/inheritor of an ordinary public class, or is it also a nested class?
  3. Finally, what kind of nested classes are we using in all these situations?
If you answer all these questions, there will be so many possible answers that your head will spin :) As you know, in order to solve a complex problem, you need to divide it into simpler parts. We will do so. Let's look at each group of nested classes in turn from two perspectives: who can inherit from this type of nested class, and from whom it can inherit. Let's start with static nested classes.

Static nested classes

Examples of Inheritance of Inner Classes - 2Their inheritance rules are the simplest. Here you can do almost anything you want. A static nested class can be inherited from:
  • regular class
  • a static nested class that is declared in the outer class or its ancestors
Recall the example from the lecture 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 static nested class Drawingand 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 take out the class altogether Drawingand make it an ordinary public class instead of a static nested one - 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;
       }
   }
}
Got it sorted out. And what classes can be inherited from a static nested one? Practical any! Nested/regular, static/non-static, it doesn't matter. Here we inherit the inner class Boeing737Drawingfrom the static nested one 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 Drawing {

   }

   public class Boeing737Drawing extends Drawing {

       public int getMaxPassengersCount() {

           return maxPassengersCount;
       }
   }
}
You can create an instance Boeing737Drawinglike 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 class Boeing737Drawinginherits from a static class, it is not static itself! So it will always need an instance of the outer class. We can take a class Boeing737Drawingout of a class Boeing737and make it just a public class. Nothing will change - it can also be inherited from a static nested Drawing.
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: in this case, we need to make the static variable maxPassengersCountpublic. If it remains private, the normal public class will not have access to it. We figured out the static classes! :) Now let's move on to inner classes. As you remember, there are 3 types of them: simple inner classes (inner classes), local classes and anonymous inner classes. Examples of Inheritance of Inner Classes - 3Again, let's move from simple to complex :)

Anonymous Inner Classes

An anonymous inner class cannot inherit from another class. No other class can inherit from an anonymous class. Easier nowhere! :)

Local classes

Local classes (in case you forgot) are declared inside the code block of another class. Most often - inside some method of this external class. Logically, only other local classes within the same method (or block) can inherit from 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 {


       }

       //...code валидации номера
   }
}
This is the code from our lecture on local classes. Inside the number validator class, we have a local class PhoneNumber- a phone number. If for our purposes we need to separate two separate entities from it, for example, a mobile phone number and a landline phone number, we can do this only inside the same method. The reason is simple: the scope of a local class is within the method (block) where it is declared. Therefore, we cannot somehow use it from the outside (including for inheritance). However, the possibilities for inheritance from the local class itself are wider! A local class can inherit from:
  1. Regular class.
  2. An inner class that is declared in the same class as the local class or in its ancestors.
  3. From another local class declared in the same method (block).
The first and third points look obvious, but the second one is a bit confusing :/ Consider two examples. Example 1 - "inheriting a local class from an inner class that was 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);
           }
       }

       //...code валидации номера
   }
}
Here we have taken the class PhoneNumberout of the method validatePhoneNumber()and made it internal instead of local. This does not prevent us from inheriting 2 of our local classes from it. Example 2 - "...or in the ancestors of this class." Here it is more interesting. We can take it PhoneNumbereven further up the inheritance chain. Let's declare an abstract class AbstractPhoneNumberValidatorthat will become an ancestor of our PhoneNumberValidator:
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 not only declared it, but also moved the inner class into it PhoneNumber. However, in its descendant class - PhoneNumberValidator- local classes in methods can easily inherit from PhoneNumber!
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);
           }
       }

       //...code валидации номера
   }
}
Due to the relationship through inheritance, local classes inside a descendant class "see" inner classes inside an ancestor. And finally, let's move on to the last group :)

Inner classes

An inner class can inherit from another inner class declared in the same outer class (or its descendant). Let's take a look at our bicycle example from the lecture on inner classes.
public class Bicycle {

   private String model;
   private int mawWeight;

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

   public void start() {
       System.out.println("Go!");
   }

   class Seat {

       public void up() {

           System.out.println("Сидение поднято выше!");
       }

       public void down() {

           System.out.println("Сидение опущено ниже!");
       }
   }

   class SportSeat extends Seat {

       //...methods
   }
}
Here, inside the class, we Bicycledeclared an inner class Seat- the seat. A special subspecies of racing seats was inherited from him - SportSeat. However, we could create a separate type of "racing bikes" and put it in a separate class:
public class SportBicycle extends Bicycle {

   public SportBicycle(String model, int mawWeight) {
       super(model, mawWeight);
   }


   class SportSeat extends Seat {

       public void up() {

           System.out.println("Сидение поднято выше!");
       }

       public void down() {

           System.out.println("Сидение опущено ниже!");
       }
   }
}
That's also possible. The inner child class ( SportBicycle.SportSeat) "sees" the inner classes of the ancestor and can inherit from them. Inheritance from inner classes has one very important feature! In the previous two examples, we SportSeathad an internal. But what if we decide to make it SportSeata regular public class that inherits from the inner class Seat?
//ошибка! No inclosing instance of  type 'Bicycle' is in scope
class SportSeat extends Bicycle.Seat {

   public SportSeat() {

   }

   public void up() {

       System.out.println("Сидение поднято выше!");
   }

   public void down() {

       System.out.println("Сидение опущено ниже!");
   }
}
We got an error! Can you guess what it has to do with it? :) Everything is simple. When we talked about the inner class Bicycle.Seat, we mentioned that a reference to an object of the outer class is implicitly passed to the constructor of the inner class. Therefore, without creating an object, Bicycleyou cannot create an object Seat. What about creation SportSeat? It does not have the built-in mechanism for implicitly passing a reference to an outer class object in the constructor as in Seat. However, without an object Bicycle, we, just as in the case of Seat, cannot create an object SportSeat. Therefore, we have only one thing left - to pass SportSeata reference to the object Bicycleexplicitly to the constructor. Here's how it's done:
class SportSeat extends Bicycle.Seat {

   public SportSeat(Bicycle bicycle) {

       bicycle.super();
   }

   public void up() {

       System.out.println("Сидение поднято выше!");
   }

   public void down() {

       System.out.println("Сидение опущено ниже!");
   }
}
To do this, we use a special word super(); Now, if we want to create an object SportSeat, nothing will prevent 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, the lecture turned out to be rather big :) But you learned a lot of new things! Examples of Inheritance of Inner Classes - 4And now it's time to solve some problems! :)
Comments
TO VIEW ALL COMMENTS OR TO MAKE A COMMENT,
GO TO FULL VERSION