A nested class is a class written inside another, in order to prevent outside classes to access it or use it (improved encapsulation) and create object models meant to be used only by one class. There are four types of nested classes, which will be described in this article.
This article is part of a series on advanced Java concepts, also corresponding to topics to study for upgrading from level I certification to level II certification (OCP level). The new Oracle Java certification framework (as from Java 11) only offers one certification covering both levels, so you’ll want to go through the level I (“OCA”) topics as well.
Types of nested classes in Java
Nested classes can be of four types:
- static
- member aka non-static, working in an instance context
- local (inside a method, like “local variables”)
- anonymous (that is the implementation of an interface or an extension of a class instantiated immediately)
The first two types are found inside a class. If they’re public, protected or package accessible, external classes might use them. If they’re private, they’re just used inside the outer class.
Static nested class
Public static nested class example
public class TheOuterClass {
public static class TheNestedClass {
// some class definition
}
}
-------------------------------
TheOuterClass.TheNestedClass nested = new TheOuterClass.TheNestedClass();
Private static nested class example
public class TheOuterClass {
public static void createInstance() {
// some code instantiating new TheNestedClass();
}
private static class TheNestedClass {
// class definition
}
}
-------------------------------
TheOuterClass.createInstance(); // the nested class cannot be accessed nor instantiated from outside the outer class
Nested class can also exist just to separate some logic and allow the outer class to create objects of their type for code clarity and ease of use, without exposing anything outside the class (which means it’s not mandatory in any way to create a factory method).
The nested class can define static and instance members (first can be called with the nested class name alone, the others need an instance of it).
Static nested classes can access all, including private, static methods and variables of their outer class. Access to the members of the nested class depends on the access modifiers.
The use of “this” inside a nested class refers to the class itself and not the outer class.
Member (instance) nested class
That kind of nested class is related to the context of an instance of the outer class. To create an instance of the inner class you must create an instance of the outer class.
It can access all members of the outer class, either static or instance, even private. Access to members of the nested class depends on the access modifiers.
The inner class can also be set private, and then will on be instantiated inside the outer class (as previously shown with static nested classes), but remember you must create an instance of the outer class to be able to create an instance of the inner class!
Inside the inner class, we cannot define static members, an enum or an interface.
Gotcha! If defining fields with the same name in the outer and inner class, the fields are shadowed in the inner class. However, you can explicitly call the outer class member like TheOuterClass.this.theField.
public class TheOuterClass {
public class TheNestedClass {
// some class definition
}
}
-------------------------------
TheOuterClass outer = new TheOuterClass();
TheOutlerClass.TheNestedClass inner = outer.new TheNestedClass();
Local class
A local class is a class inside a method. As per the Oracle University course on Java SE 11, that’s a very rare use case. Still you have to know it for the certification.
The local class is declared inside a method and can only be used in the context of that method. Instances can only be created in within the method context.
The local class can access the variables in the outer class. It can see the local (method) variables, including parameters, if they’re final or effectively final (compiler checks if the value is assigned once and never changed).
public class TheOuterClass {
void theMethod() {
class TheLocalInner {
// class definition
}
TheLocalInner inner = new TheLocalInner();
// ... using the inner instance
}
public static void main(String[] args) {
TheOuterClass outer = new TheOuterClass();
outer.theMethod();
}
}
Remember that local members don’t have access modifiers, their scope is the block they’re in! Same goes for the local class.
No static members, enum or interface can be defined in the local inner class.
Anonymous class
The most common type of nested class (still according to Oracle University). It’s the inline implementation of an interface or the extension of a class (concrete or abstract) instantiated immediately. The anonymous always implements an interface or extends a class, but without using the keywords, just by calling new on the class or interface it extends/implements.
They’re called anonymous because they don’t have a named identifier.
Usual rules about overriding methods apply.
OrderI order = new OrderI(){
@Override
public void doStg() {
// ...
}
};
order.doStg();
The anonymous class has access to the outer class variables and the local final or effectively final variables. Using the same name for a local field will shadow the outer field.
It can come handy for example if you need to use an interface once and don’t want to create a whole implementation for a unique use.
Anonymous class cannot have static members (except static final) or initializers, interface declarations or constructors. On the other hand, an instance initializer can be used to initialize fields of the anonymous class.
Anonymous class as a parameter
Collections.sort(List.of(1, 3, 2), new Comparator<Integer>() {
@Override
public int compare(Integer o1, Integer o2) {
return o1.compareTo(o2);
}
}
);
Functional interfaces can be replaced by lambda expressions, which is further discussed in dedicated article. Here’s a first glimpse, doing exactly the same as previous implementation:
Collections.sort(List.of(1, 3, 2), (o1, o2) -> o1.compareTo(o2));
Using a functional interface as parameter is a very common use case for anonymous classes, making them widely used.
Check other OCA (level I) and OCP (level II) certification topics articles and subscribe to receive future posts in your mailbox:
One thought on “Nested classes – Java advanced (OCP)”