JavaRush/Java Blog/Random EN/Inheritance of nested classes

Inheritance of nested classes

Published in the Random EN group
members
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 one. If not, believe me: this 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 successor/inherited a regular public class, or is it also a nested class?
  3. Finally, what exactly type of nested classes are we using in all of 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. That's what we'll do. 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 internal classes - 2Their rules of inheritance are the simplest. Here you can do almost anything your heart desires. A static nested class can be inherited from:
  • regular class
  • a static nested class that is declared in the outer class or its ancestors
Let's remember the example from the lecture about 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 remove the class altogether Drawingand make it a regular 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;
       }
   }
}
That's sorted out. And what classes can inherit from a static nested one? Almost 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 itself is not static! Therefore it will always need an instance of the outer class. We can take the class Boeing737Drawingout of the class Boeing737and make it just a public class. Nothing will change - it can also inherit 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've sorted out static classes! :) Now let's move on to inner classes. As you remember, there are 3 types of them: simply inner classes, local classes and anonymous inner classes. Examples of inheritance of internal 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. It couldn't be simpler! :)

Local classes

Local classes (in case you forgot) are declared inside a code block of another class. Most often - inside some method of this outer class. It is logical that only other local classes within the same method (or block) can inherit from a local class. Here's 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 about local classes. Inside the number validator class we have a local class PhoneNumber- phone number. If for our purposes we need to extract two separate entities from it, for example, a mobile phone number and a landline phone number, we can only do this within the same method. The reason is simple: the scope of a local class is within the method (block) where it is declared. Therefore, we will not be able to somehow use it externally (including for inheritance). However, the local class itself has wider possibilities for inheritance! 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 is a little confusing :/ Let's look at 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 our 2 local classes from it. Example 2 – “...or in the ancestors of this class.” This is where it gets more interesting. We can take it PhoneNumbereven higher up the chain of inheritance. Let's declare an abstract class AbstractPhoneNumberValidatorthat will become the ancestor of ours 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 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 валидации номера
   }
}
Thanks to the connection through inheritance, local classes inside a descendant class “see” inner classes inside the ancestor. And finally, let's move on to the last group :)

Inner classes

An inner class can be inherited by another inner class declared in the same outer class (or its descendant). Let's look at this using 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
   }
}
BicycleHere we declared an internal class inside the class Seat- seat. A special subtype of racing seats was inherited from it - 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("Сидение опущено ниже!");
       }
   }
}
This is also possible. The child's inner class ( SportBicycle.SportSeat) “sees” the ancestor's inner classes and can inherit from them. Inheritance from inner classes has one very important feature! In the previous two examples we SportSeathad internal. But what if we decide to make it SportSeata regular public class, which also 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 is connected with? :) It's simple. When we talked about the inner class Bicycle.Seat, we mentioned that the constructor of the inner class implicitly passes a reference to an object of the outer class. Therefore, without creating an object, Bicycleyou cannot create an object Seat. What about creation SportSeat? It does not have the same built-in mechanism for implicitly passing a reference to an outer class object in the constructor as in Seat. However, without an object Bicycle, just like in the case with Seat, we cannot create an object SportSeat. Therefore, we have only one thing left to do - explicitly pass SportSeata reference to the object to the constructor. BicycleHere'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("Сидение опущено ниже!");
   }
}
For this we use a special word super(); Now, if we want to create an object SportSeat, 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, the lecture turned out to be quite big :) But you learned a lot of new things! Now is the time to solve a few problems! :)
Comments
  • Popular
  • New
  • Old
You must be signed in to leave a comment
This page doesn't have any comments yet