5.2 Nested Types: Member Inner, Static Nested, Local, and Anonymous Classes
Key Takeaways
Java defines four nested class varieties: static nested classes, member inner classes, local inner classes, and anonymous inner classes, each with distinct scoping, lifecycle, and enclosing instance requirements.
Static nested classes behave like top-level classes in an outer namespace and require no outer instance, whereas member inner classes are tied to an enclosing instance and use outerRef.new Inner() syntax.
Java 16 relaxed legacy restrictions, permitting all inner class forms (member inner, local, and anonymous) to declare static members, including static fields, methods, records, and interfaces.
Local and anonymous classes can only access local variables and parameters from their enclosing method or block if those variables are final or effectively final (never modified after initialization).
Top-level and nested classes form a nest (JEP 181), enjoying bidirectional access to each other's private members without synthetic bridge methods.
Nested Types: Member Inner, Static Nested, Local, and Anonymous Classes
Java allows developers to declare a class within another enclosing class or code block. These are known as nested classes. Grouping related classes within their logical context increases encapsulation, enhances maintainability, and prevents namespace pollution.
Java categorizes nested classes into two primary divisions:
- Static Nested Classes: Declared with the
staticmodifier at the class member level. - Inner Classes (Non-Static): Further divided into three types:
- Member Inner Classes: Declared at the class member level without
static. - Local Classes: Declared inside a method, constructor, or code block.
- Anonymous Inner Classes: Declared and instantiated inline within an expression without a name.
- Member Inner Classes: Declared at the class member level without
Architectural Comparison of the Four Nested Types
The following table summarizes the structural properties and constraints of each nested class type:
| Nested Class Type | Scope & Declaration | Outer Instance Reference? | Allowed Access Modifiers | Can Declare Static Members? (Java 16+) | Explicit Constructors? |
|---|---|---|---|---|---|
| Static Nested | Enclosing class member level (static) | No (static context) | public, protected, default, private | Yes | Yes |
| Member Inner | Enclosing class member level (non-static) | Yes (Outer.this) | public, protected, default, private | Yes | Yes |
| Local Inner | Inside method, constructor, or block | Yes (if enclosing context is non-static) | None (package/block-private only) | Yes | Yes |
| Anonymous | Inline within expression (unnamed) | Yes (if enclosing context is non-static) | None | Yes | No (instance initializers only) |
Static Nested Classes
A static nested class is essentially a top-level class that has been packaged inside the namespace of an enclosing class for organizational convenience. It does not hold an implicit reference to an instance of the outer class.
Instantiation Syntax
Because no outer object instance is required, a static nested class is instantiated directly using the outer class's name:
OuterClass.StaticNested nested = new OuterClass.StaticNested();
If imported statically or accessed from within OuterClass, the prefix can be omitted:
StaticNested nested = new StaticNested();
Access Boundaries
Static nested classes can directly access all static members (methods and fields) of the enclosing class, including private static members. However, they cannot directly access instance variables or instance methods of the enclosing class because there is no implicit this reference pointing to an outer object:
public class OuterClass {
private static int staticData = 100;
private int instanceData = 200;
public static class StaticNested {
public void printValues() {
System.out.println(staticData); // VALID: accesses private static member
// System.out.println(instanceData); // COMPILE ERROR: non-static field cannot be referenced from static context
System.out.println(new OuterClass().instanceData); // VALID: accessed via explicit outer instance
}
}
}
Member Inner Classes
A member inner class is declared within the body of an enclosing class without the static modifier. Unlike static nested classes, every instance of a member inner class is bound to a specific instance of the enclosing class.
Instantiation Mechanics
To instantiate a member inner class, an enclosing instance of OuterClass must already exist. The instantiation syntax reflects this outer-object dependency:
// From outside the outer class or from a static method:
OuterClass outer = new OuterClass();
OuterClass.InnerClass inner = outer.new InnerClass();
// Or combined in a single statement:
OuterClass.InnerClass inner2 = new OuterClass().new InnerClass();
Attempting to instantiate a member inner class from a static method using new InnerClass() triggers a compile error: non-static variable this cannot be referenced from a static context. However, from within an instance method of OuterClass, the implicit this reference is automatically supplied:
public class OuterClass {
public class InnerClass {}
public void createInner() {
InnerClass inner = new InnerClass(); // Valid: uses this.new InnerClass()
}
}
Disambiguating References with Qualified this
When a member inner class declares a member or parameter with the same name as a member in the enclosing class, shadowing occurs. To reference the outer instance's member, use the qualified this syntax: OuterClass.this.memberName:
public class Ship {
private String name = "Nautilus";
public class Engine {
private String name = "Diesel";
public void printNames(String name) {
System.out.println(name); // Method parameter
System.out.println(this.name); // Inner class field ("Diesel")
System.out.println(Ship.this.name); // Outer class field ("Nautilus")
}
}
}
The Java 16 Relaxation for Static Members
Prior to Java 16, non-static inner classes were strictly forbidden from declaring static members, with the sole exception of compile-time constant expressions (static final primitive or String fields).
Beginning in Java 16 (JEP 395) and fully active in Java 17, this restriction was lifted. Inner classes can now declare static members, including static fields, static methods, static initializers, records, and interfaces:
public class Outer {
public class Inner {
static int globalCount = 0; // Legal in Java 16+!
static void reset() { globalCount = 0; } // Legal in Java 16+!
record Point(int x, int y) {} // Legal in Java 16+!
}
}
Warning
Outdated Study Guide Trap: Static in Inner Classes Older prep books and Java 8/11 guides claim that inner classes can never declare static methods or non-constant static fields. On the Java 17 (1Z0-829) exam, remember that Java 16+ permits member inner classes, local classes, and anonymous classes to declare static members!
Local Inner Classes
A local class is declared directly inside a block of executable code, such as a method body, constructor, or for loop.
Scoping and Modifiers
- Visibility: A local class is visible only within the block in which it is defined, starting from its declaration line downward.
- Modifiers: Because they are local to a block, local classes cannot have access modifiers (
public,protected,private). They also cannot be declaredstatic. They can, however, be markedabstractorfinal.
Variable Capture and Effectively Final Constraints
A local class can read fields from the enclosing outer class (both instance and static, including private). However, when accessing local variables or parameters declared in the enclosing method, a strict rule applies: the local variable must be final or effectively final.
An effectively final variable is one whose initial value is never changed after assignment:
public void process(int multiplier) {
int base = 10;
int counter = 0;
counter++; // counter is NOT effectively final because it is reassigned
class Calculator {
void execute() {
System.out.println(base * multiplier); // VALID: both are effectively final
// System.out.println(counter); // COMPILE ERROR: local variable referenced from inner class must be final or effectively final
}
}
new Calculator().execute();
}
If a variable is reassigned anywhere in the enclosing method—even after the local class declaration—it loses its effectively final status and triggers a compilation failure.
Anonymous Inner Classes
An anonymous class is a local inner class without a name. It is defined and instantiated simultaneously in a single expression using the new operator.
Syntax and Characteristics
Comparator<String> comp = new Comparator<String>() {
@Override
public int compare(String s1, String s2) {
return Integer.compare(s1.length(), s2.length());
}
};
Key architectural rules governing anonymous classes:
- No Constructors: Because constructors must bear the name of their class, anonymous classes cannot declare constructors. Any required instance initialization must occur via an instance initializer block (
{ ... }). - Single Extension/Implementation: An anonymous class can extend exactly one class OR implement exactly one interface. It cannot specify an
extendsorimplementsclause, nor can it implement multiple interfaces. - Effectively Final Capture: Like local classes, anonymous classes can capture enclosing local variables only if they are
finalor effectively final. - Interaction with
var: When assigned tovar, members declared uniquely within the anonymous class body remain accessible because the compiler infers the exact anonymous type:
var runner = new Object() {
int count = 5;
void log() { System.out.println("Count: " + count); }
};
runner.log(); // Compiles cleanly via non-denotable type inference!
Nestmates and Private Member Access (JEP 181)
Top-level classes and all nested types within their compilation unit form a nest. Under the Java nestmate architecture (introduced in Java 11 via JEP 181), an outer class and all its nested classes (static, member, local, and anonymous) share mutual access to each other's private members without compiler-generated synthetic bridge methods (access$000), ensuring complete and bidirectional encapsulation across the nest.
Given the following class definition:
class Company {
private String name = "TechCorp";
class Department {
private String deptName;
Department(String deptName) { this.deptName = deptName; }
void printInfo() {
System.out.println(Company.this.name + " - " + deptName);
}
}
}
Which of the following code snippets correctly instantiates a Department object from a main method located outside Company?
Department dept = Company.new Department("Engineering");
Company.Department dept = new Company.Department("Engineering");
Company.Department dept = new Company().new Department("Engineering");
Company.Department dept = new Company.Department(new Company(), "Engineering");
Consider the following code snippet:
public class CalculationDemo {
public static void main(String[] args) {
int multiplier = 2;
int base = 5;
class MathOp {
int run() {
return base * multiplier;
}
}
multiplier = 3;
MathOp op = new MathOp();
System.out.println(op.run());
}
}
What is the result of attempting to compile and run this code?
Compilation fails at multiplier = 3; because local variables accessed by inner classes are implicitly final
Compiles successfully and prints 10
Compiles successfully and prints 15
Compilation fails inside MathOp.run() because multiplier is not effectively final
Consider the following class compiled under Java SE 17:
public class OuterContainer {
private static int staticVal = 10;
private int instanceVal = 20;
public static class StaticNested {
public void show() {
System.out.print(staticVal + " ");
System.out.print(instanceVal); // Line X
}
}
public class MemberInner {
static int innerStatic = 50; // Line Y
public void show() {
System.out.print(OuterContainer.this.instanceVal + " " + innerStatic);
}
}
}
What is the outcome of compiling this file under Java SE 17?
Compilation fails at Line Y because member inner classes are prohibited from declaring static fields.
The file compiles cleanly because static nested classes can access all private members of the enclosing class.
Compilation fails at both Line X and Line Y.
Compilation fails at Line X because a static nested class cannot directly access instance members of the enclosing class.
What is the output of running the following Java program?
public class Window {
private int size = 100;
public class Panel {
private int size = 20;
public void printSizes(int size) {
System.out.print(size + " ");
System.out.print(this.size + " ");
System.out.print(Window.this.size);
}
}
public static void main(String[] args) {
new Window().new Panel().printSizes(5);
}
}
5 5 100
Compilation fails because Window.this cannot be resolved inside printSizes
5 20 100
20 20 100
Sections you finish are checked off in the contents.