Nested Classes
Sometimes a small class is needed only as part of another class: for example, it describes an internal detail, helps perform one narrow task, or only makes sense next to the outer class. Instead of creating a separate file, such a class can be declared inside another class.
Such classes are called nested classes. This is not a separate inheritance or encapsulation mechanism, but a way to keep related types closer together and limit the scope of their use more precisely.
Static nested class
If a nested class is declared with static, it does not need an object of the outer class.
It is associated with the outer class mainly logically: the outer class name becomes part of its full name.
class Order {
private final int id;
Order(int id) {
this.id = id;
}
static class Validator {
boolean isValid(Order order) {
return order.id > 0;
}
}
}
You can create such an object through the outer class name:
Order.Validator validator = new Order.Validator();
boolean valid = validator.isValid(new Order(10));
Validator has no implicit reference to a particular Order,
so it accesses the outer class's instance state only through an explicitly supplied object.
At the same time, as a nested class it can access even private members of Order.
Inner class
A non-static nested class is called an inner class. Its object is associated with a particular object of the outer class and can directly use its fields and methods.
class Counter {
private int value;
class Incrementer {
void increment() {
value++;
}
}
}
That is why you first need an outer object and then create the inner object through it:
Counter counter = new Counter();
Counter.Incrementer incrementer = counter.new Incrementer();
incrementer.increment();
Here, incrementer is associated specifically with the counter object.
If you create a second Counter, its inner object will work with the state of that second instance instead.
static here does not determine whether static members are allowed.
Modern Java allows inner classes to declare static members.
The key difference is elsewhere: an inner class is associated with an outer-class instance, while a static nested class is not.
Local class
A class can be declared in an even narrower place — directly inside a method or another block. Such a local class is visible only in the scope where it is declared, so it works well for small helper logic that is not needed anywhere else.
void printMessage(String prefix) {
class Printer {
void print(String text) {
System.out.println(prefix + text);
}
}
new Printer().print("Ready");
}
A local class can use variables from the surrounding method if they are declared
final or are effectively final — that is, they are not changed after assignment.
In the example, the prefix parameter is not changed, so it is available inside Printer.
Anonymous class
If the class will not need a separate name at all, you can declare the class and immediately create its object in a single expression. This is an anonymous class. It is most often used to create a one-off implementation of an interface or a small specialized subclass of an existing class.
interface Greeter {
void greet();
}
Greeter greeter = new Greeter() {
@Override
public void greet() {
System.out.println("Hello");
}
};
greeter.greet();
After new Greeter() comes the body of a new unnamed class
that implements Greeter. An anonymous class cannot have its own constructor
because it has no name. Like a local class, it can use only
final or effectively final local variables from the surrounding scope.
How to choose the right option
- Static nested class — when the type logically belongs to the outer class but should not be associated with a particular outer object.
- Inner class — when the nested object needs direct access to the state of a particular outer object.
- Local class — when a named helper type is needed only inside one method or block.
- Anonymous class — when you need a one-off implementation or specialization and a separate name would only get in the way.
In practice, it is usually best to start with a static nested class: a connection to an outer instance is not always needed, and an unnecessary implicit connection makes the object model more complicated. Choose an inner, local, or anonymous class when its tighter connection to the surrounding code is genuinely useful.