Hi! Today we'll look at an important mechanism: inheritance in nested classes.
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.
Their inheritance rules are the simplest. Here you can do almost anything your heart desires.
A static nested class can inherit:
Again, let's move from simple to complex :)
- Are we making a nested class inherit some class? Or are we making some class inherit a nested class?
- Is the child/parent class an ordinary public class, or is it also a nested class?
- Finally, what type of nested classes do we use in all these situations?
Static nested classes

- an ordinary class
- a static nested class that is declared in an outer class or its ancestors
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.

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:
- An ordinary class.
- An inner class that is declared in the same class as the local class or in one of its ancestors.
- Another local class declared in the same method (code block).
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!
Now it's time to solve some tasks! :)
GO TO FULL VERSION