If you would like to create your own superhero, what would you do? Of course, you would open a favorite development environment and create a class Superhero!
To be a proper superhero, our character will need a set of special items, including, for example, a magic cloak or a hammer. Here we may have a problem. What is the best way to organize the classes describing the equipment? And how can we mark that only one class, that is Superhero, can use them?
Here appears our savior — an instrument called nested classes. They help us group classes logically and increase the encapsulation of our code.
What is a nested class?
Basically, you can call a class nested when it is declared inside another class.
This is what our superhero would look like:
class Superhero {
class MagicCloak {
}
class Hammer {
}
}
Both classesMagicCloak and Hammer are nested classes. The Superhero class is often called an outer class, and a nested class is called a member of an outer class.
In this topic, we are going to talk about non-static nested classes, which are commonly known as inner classes.
Inner class
Let's move on to another example. Imagine that you are writing a class Cat representing cats. The Cat class may have a lot of fields and methods, but we may also use inner class structures. For example, let's say you want a cat to have a bow. Then you need to create a new class Bow. This class Bow would be quite small and specific, and you know you won't need a bow without a cat. The solution is to create the class Bow inside the Cat class:
public class Cat {
private String name;
public Cat(String name) {
this.name = name;
}
public class Bow {
private String color;
public Bow(String color) {
this.color = color;
}
public void printColor() {
System.out.println("Cat " + Cat.this.name + " has a " + this.color + " bow.");
}
}
}
Let's create a cat Bob with a red bow:
public class Main {
public static void main(String[] args) {
Cat cat = new Cat("Bob");
Cat.Bow bow = cat.new Bow("red");
bow.printColor();
}
}
Look, we have created an instance of Cat and then created an instance of Bow using quite an interesting syntax.
Here, the output will be:
Cat Bob has a red bow.
Remember that to use inner classes, we must create an instance of the outer class. In our example, we created a Cat.
Scope of the inner class
Now let's discuss what we can see from the inner class and who can access the inner class from outside.
Here is our class Cat with a new method sayMeow and an inner class Bow with a new method putOnABow .
public class Cat {
private String name;
public Cat(String name) {
this.name = name;
}
private void sayMeow() {
System.out.println(this.name + " says: \"Meow\".");
}
public class Bow {
String color;
public Bow(String color) {
this.color = color;
}
public void putOnABow() {
Cat.this.sayMeow();
System.out.println("Bow is on!");
}
public void printColor() {
System.out.println("Cat " + Cat.this.name + " has a " + this.color + " bow.\n");
}
}
}
Note, that inside the method putOnABow of the class Bow, we have access to the private method sayMeow of the class Cat. And, as you can see, in the method sayMeow, we print the private field name of the class Cat and everything works fine.
Also, in the method printColor, we have direct access to the private field name of the class Cat.
How about creating a cat Princess with a golden bow to prove that our code works?
Cat cat = new Cat("Princess");
Cat.Bow bow = cat.new Bow("golden");
bow.printColor();
bow.putOnABow();
And, yes, the bow is on!
Cat Princess has a golden bow.
Princess says: "Meow".
Bow is on!
As for the access from the outside world, remember that when you've instantiated an inner class, you can do whatever you want according to access modifiers.
Now let's collect all the rules together and put them to a "hard disk"!
Rules for Inner classes
From inside the inner class, we can see all methods and fields of the outer class even if they are private. And don't forget that we can use everything else according to access modifiers as well.
An inner class is associated with an instance of its enclosing class. So to instantiate an inner class and get access to it, you need to instantiate the outer class first:
Outer outer = new Outer();
Outer.InnerClass inner = outer.new InnerClass();
Remember about access modifiers: if you make an inner class private, then it can only be accessed from inside the outer class. The same works for fields and methods.
And be careful — there are some restrictions!
Prior to Java 16, inside an inner class, you could not define:
any static members;
enums;
an Interface.
Reasons to use Inner Classes
Have you noticed what our two examples of Superhero with the magic items and Cat with a bow have in common? Sure you have — we hid our inner classes from the outside world so that only a Superhero may use a magic cloak and only a cat may put on a bow. Now, it will be easier to navigate between classes and to understand the structure of your code.
And, at last, the formal (just a little bit) list of inner class benefits:
They increase encapsulation. Our
Bowis only forCat. You can make a field (method)privateand hide it from other classes, using it only inside the inner class.Inner classes will organize your code and make your packages be more logical: all the magic equipment for
Superherowill be in one place.
Summary
You can create a class within another class, and such classes are called nested. A non-static nested class is called an inner class. We hope you are ready to use it. Just don't forget to instantiate the outer class first!
The main idea of Inner classes is to hide some code from other classes and increase encapsulation.