Proper Use of Nested Classes
What Are Nested Classes?
Nested classes, sometimes called inner classes, are classes defined inside of another class. Other than being defined inside of another class, they are usually just like any other class. The reason nested classes exist is strictly for encapsulation. Nested classes, when used right, act as their own accessibility level. If they are being used for any reason other than encapsulation, they are being misused.
Nested classes are a powerful tool for encapsulation but are misused or not used when they should be used. Having another level of indentation in your code is not pretty and is a big reason why people may avoid them. Although this is a valid reason, allowing code to be accessed where it shouldn't is a bigger problem.
Nested Class Use Cases
There are really only two use cases where you should use a nested class. The first is to encapsulate a class that is only used inside another class and not in any other class. This could be a record class or some type of helper class.
// Java code
public class OuterClass {
private class NestedClass {
private NestedClass() {
}
}
}
In this use case, the nested class may access the outer class variables and methods and vice versa, or they may not.
The other use case is to encapsulate a class type.
// Java code
public class Collection {
// ...
public class Node {
}
}
This allows you to have a type like the following shows.
final Collection.Node node;
Encapsulating a class type is used when it is always used with the outer class. In this use case, it is more common the type will be independent and not access methods and variables in the outer class and vice versa.
Controlling Creating Instances
You can control the creation of an instance a few different ways. One is through a factory. This could be in a factory class or a static factory method on the class that you are creating instances for. Another is to limit the level of accessibility the constructor has. With nested classes, you can make the constructor private and fully control the instances created in the outer class.
// Java code
public class LinkedList {
// ...
public class Node {
private Node() {
}
}
}
This is something that a static factory method or factory class cannot do because they will break encapsulation.
Inheritance
You can do some interesting things with nested classes and inheritance. The protected modifier is commonly used as a public accessibility level, but only for the classes that are in that inheritance structure. If you use protected nested classes, you can restrict their usage and the instance creation of those nested classes in the inheritance structure. This almost acts as another level of accessibility.
// Java code
public class BaseClass {
protected class NestedClass {
protected NestedClass() {
}
}
}
public class SubClass extends BaseClass {
private NestedClass nestedClass = new NestedClass();
// ...
}
Constructor Accessibility
Nested classes should be restricted to only private or protected. Private, meaning only the outer class can access it to create instances of it. Protected, meaning in an inheritance structure, only those classes can create instances of it. If you have any other level of accessibility, then you probably shouldn't be using a nested class. The nested class can have any access modifier, but the constructor should only be private or protected.
Conclusion
Nested classes are used to encapsulate a class. Use nested classes when they are only used in the class they are defined in or when the type is always associated with the instance. Don't use nested classes for any other reason. The additional level of indentation complicates the code.