Nested and Inner Classes

A complete guide to Java's nested class family: static nested classes, non-static inner classes, local classes, and anonymous classes. Covers when to use each type, how inner classes access outer members, and how anonymous classes work with interfaces.

Why Put a Class Inside Another Class?

Java allows you to define a class inside another class. The result is called a nested class, and there are four distinct varieties: static nested classes, non-static inner classes, local classes, and anonymous classes. Each solves a different problem and carries different trade-offs.

The core motivation is encapsulation and co-location. If a class is only ever meaningful in the context of one other class, embedding it there signals that relationship to the reader and keeps the codebase tidy. A LinkedList.Node has no business existing independently of LinkedList, so nesting it makes the dependency explicit. Similarly, a one-off event handler that does a single thing in a single place does not deserve its own file.

The four types at a glance

All four types live inside another class, but they differ in how they relate to the enclosing class instance.

  • Static nested class:Declared with the static modifier. Associated with the outer class, not with any instance. No implicit reference to the outer object.
  • Non-static inner class:No static modifier. Holds an implicit reference to an enclosing outer class instance. Can access all outer members directly.
  • Local class:Declared inside a method or block. Scoped to that method. Can capture effectively final local variables.
  • Anonymous class:A local class without a name. Declared and instantiated in one expression. Used for short, one-off implementations of a class or interface.

Static Nested Classes

A static nested class is declared inside another class with the static modifier. Despite living inside the outer class, it has no connection to any instance of that outer class. You can create a static nested class instance without creating an outer class instance first. To make an instance from outside the outer class, you reference it using the outer class name: OuterClass.NestedClass obj = new OuterClass.NestedClass().

Because there is no implicit outer instance, a static nested class can only access the static members of the outer class directly. It cannot reach instance fields or instance methods of the outer class unless it holds an explicit reference to an outer object. This makes static nested classes the safest choice because they do not create hidden retention of outer instances.

Common uses for static nested classes

The pattern appears in many places across the Java ecosystem.

  • Builder pattern:A Builder class is almost always a static nested class of the object it constructs (e.g., Person.Builder). The builder needs to be used independently of any existing Person instance.
  • Node or Entry types:LinkedList.Node, HashMap.Entry. These types exist solely to support the data structure they belong to and make no sense outside it.
  • Result objects:A method that returns multiple related values can return a static nested class instance instead of an Object array or a map.

Static Nested Class: The Builder Pattern

Java

HttpRequest uses a static nested Builder class that can be created independently of any existing HttpRequest instance.

Non-static Inner Classes

A non-static inner class is declared inside another class without the static modifier. Every instance of the inner class holds a hidden reference to the outer class instance that created it. This means the inner class can access all of the outer class's members directly, including private fields and private methods.

To create an inner class instance from outside the enclosing class, you first need an outer class instance, and then you call outerInstance.new InnerClass(). Inside the inner class, if a name collision exists between an outer field and an inner field, you can use OuterClass.this.fieldName to explicitly reach the outer instance's version.

The hidden reference and memory implications

The implicit outer reference is useful but has one significant drawback to be aware of.

  • The inner class instance cannot exist without an outer class instance. If you hold an inner class object in a long-lived context (such as a static field or a cache), the outer object cannot be garbage collected even if nothing else references it.
  • This is a common source of memory leaks in GUI and Android code, where an anonymous or inner class listener holds a reference to a window or Activity long after it should have been released.
  • When you need the nesting but not the implicit reference, prefer a static nested class. Only use a non-static inner class when you genuinely need direct access to the outer instance's state.

Non-static Inner Class: Access to Outer Members

Java

A BankAccount inner class Iterator that reads the outer account's private transaction list directly.

Accessing Outer Class Members from an Inner Class

Non-static inner classes can see all members of the enclosing class: public or private, instance or static. When there is no name conflict, you refer to outer members directly, just as though they were declared in the inner class itself. When there is a conflict (an inner field shadows an outer field with the same name), use the qualified form OuterClass.this.memberName to reach the outer version.

This access follows the same rules as any other access: even private members of the outer class are visible to the inner class because they are in the same compilation unit. The compiler generates the necessary synthetic accessor methods to make this work behind the scenes.

Name Shadowing and OuterClass.this

Java

An inner class field shadows an outer field with the same name. OuterClass.this resolves the ambiguity.

Local Classes

A local class is a class declared inside a method body (or any block, such as an if statement or a loop). Its scope is entirely limited to that block. Once the method returns, the class name is no longer accessible, just like a local variable.

Local classes can access the enclosing method's local variables, but only if those variables are effectively final. A variable is effectively final if it is never reassigned after its initial assignment, even without the final keyword. This restriction exists because the local class instance may outlive the method call (if a reference to it is returned or stored), and capturing a mutable local variable would cause unpredictable behaviour.

Local class vs anonymous class

Local classes and anonymous classes are close relatives. The key differences are practical.

  • Local class:Has a name, can have a constructor, and can be instantiated multiple times within its scope. Use it when you need several instances or a constructor with parameters.
  • Anonymous class:No name, no separate constructor. Instantiated exactly once at the point of declaration. Use it for one-off, short implementations.
  • Both capture effectively final variables:The compiler enforces this for both types.

Local Class Inside a Method

Java

A Validator local class defined and used entirely within a single method, capturing an effectively final threshold.

Anonymous Classes

An anonymous class is a special shorthand: you declare a class and create exactly one instance of it in a single expression. Because the class has no name, you cannot instantiate it a second time using a constructor call. The syntax looks like a constructor call followed immediately by a class body in curly braces:

new SomeClassOrInterface() { /* override methods here */ }

When you write new SomeInterface() with a body, you are creating an anonymous class that implements the interface. When you write new SomeClass() with a body, you are creating an anonymous class that extends it.

Since Java 8, lambda expressions can replace anonymous classes when the interface has exactly one abstract method (a functional interface). But anonymous classes remain necessary when you are extending a concrete class, implementing a non-functional interface, or needing to hold state in fields.

Anonymous Class Extending a Class

Java

An anonymous subclass of Thread overrides run() for a single, nameless execution unit.

Anonymous Class with Interface

The most common use of anonymous classes is implementing an interface on the spot. Before lambda expressions arrived in Java 8, this was the standard way to write event handlers, comparators, and callbacks. Even now, it is still the right tool when you need an interface implementation that:

- carries state in its own fields, or

- implements an interface that has more than one abstract method (which rules out lambdas).

Anonymous Class Implementing an Interface

Java

Implementing Comparator with an anonymous class, side-by-side with the equivalent lambda for comparison.

Anonymous Class with Multiple Methods

Java

Implementing a two-method interface shows why a lambda cannot be used here.

When to Use Each Type

Choosing the right kind of nested class is a design decision. Getting it wrong does not usually break the program, but it can make the code harder to read, cause subtle memory leaks, or expose more of the API surface than intended. The following guidelines cover the most common situations.

Use a static nested class when...

This should be your default choice when nesting classes.

  • The nested class is logically part of the outer class concept but does not need to talk to a specific outer instance. Builder, Node, Entry, and Result types almost always fall into this category.
  • You want to avoid the memory retention risk that comes with the implicit outer reference.
  • You need to expose the type as a public or package-private API without requiring callers to hold an outer instance.

Use a non-static inner class when...

Reserve this for cases where the tight coupling to the outer instance is genuinely needed.

  • The nested class represents something that cannot conceptually exist without its enclosing object. An Iterator that walks the internals of a specific collection instance is the textbook example.
  • The nested class needs to call private methods or read private fields of the outer instance repeatedly, and passing the outer reference explicitly would be more verbose without adding clarity.

Use a local class when...

Local classes are rare in modern code but useful in specific situations.

  • You need a helper class that is only relevant inside one method and you want to instantiate it more than once within that method.
  • You need a constructor with parameters or state that justifies more structure than an anonymous class provides.
  • You want the class definition separated from the instantiation point for readability, but still scoped within the method.

Use an anonymous class when...

Anonymous classes shine for concise, one-off implementations.

  • You need to implement or extend something exactly once and the implementation is short enough to read comfortably inline.
  • The interface has more than one abstract method, ruling out a lambda expression.
  • You need the implementation to carry its own fields or hold state that is specific to this single instance.
  • You are working in a pre-Java 8 codebase or with a framework that expects full class bodies rather than lambdas.

Choosing the Right Type: A Side-by-Side Example

Java

Four scenarios, each using the appropriate class type.

Quiz - Test Your Knowledge

Ten questions covering static nested classes, non-static inner classes, local classes, anonymous classes, the effectively-final rule, memory implications, and the relationship between anonymous classes and lambda expressions. Read each option carefully before selecting your answer.

Knowledge Check

1. Which of the following is true about a static nested class in Java?

2. How do you instantiate a non-static inner class from outside the enclosing class?

3. What is a local class in Java?

4. Which constraint applies to local variables captured by a local class or anonymous class?

5. What is an anonymous class?

6. A non-static inner class holds a hidden reference to its enclosing outer class instance. What is the most significant practical consequence of this?

7. When would you choose a static nested class over a non-static inner class?

8. Which statement about anonymous classes and lambda expressions is correct?

9. Inside a non-static inner class, how do you explicitly reference the enclosing outer class instance?

10. Which type of inner class is best suited for implementing a one-off event listener directly inside a method, when the implementation is short and not needed anywhere else?