Java Inner and Anonymous Classes

A Java inner class is a non-static nested class associated with an enclosing object.

Lesson 41 of 127Intermediate14 min read

A Java inner class is a non-static nested class associated with an enclosing object. An anonymous class creates one unnamed implementation at an expression. These forms help keep small helper types near the code they support, but their enclosing references and captured variables deserve careful attention.

Review the related foundation lesson when you need a prerequisite. The complete programs below use Java 17-compatible syntax.

Enclosing instances

A non-static inner class is associated with an enclosing object and can access its instance state. Creating one outside the outer class uses outer.new Inner().

A static nested class belongs inside another class declaration without automatically holding an enclosing instance. A non-static inner class does hold that association and can access the enclosing object's instance members. Construct a member inner class through a particular outer object, such as outer.new Reader(). The same inner class declaration can therefore produce helpers connected to different outer objects.

Static nested types and qualified this

A static nested class has no implicit enclosing instance. It is often preferable when the nested type does not need access to outer object state.

When inner and outer members share a name, this identifies the inner object and Outer.this identifies its enclosing outer object. This distinction matters because there are two receivers, not because Java renamed one field. Avoid unnecessary shadowing in routine designs, but learn the qualified form so existing nested code remains understandable.

Anonymous implementations

An anonymous class defines and creates an unnamed implementation in one expression. It can extend a class or implement an interface.

An anonymous class expression declares and constructs a class in one place, usually implementing an interface or extending a class. It can override the required methods and declare its own members, but it has no ordinary named constructor declaration. Use it when one small implementation deserves a local home. For a functional interface, a lambda may express the behavior more briefly, with different this semantics.

Captured local variables

Captured local variables must be final or effectively final. A lambda is usually simpler for a functional interface, but an anonymous class has its own this and can declare additional members.

A local or anonymous class may capture local variables that are final or effectively final. Effectively final means the variable is never reassigned after its initialization. Capturing a mutable object reference does not freeze that object's contents. An inner object can also keep its outer object reachable longer than expected, so prefer a static nested helper when the enclosing instance is unnecessary.

Running the examples

Save name.java, compile it with javac --release 17 name.java, and run java name. Use each program's own public class name and filename.

How GardenInnerClasses produces its output

Garden stores Fern House as its name. garden.new Sign associates the sign with that garden, so text can read the enclosing name. The anonymous Runnable captures the effectively final sign reference and implements run. Calling announcement.run executes that body synchronously and prints Welcome to Fern House; it does not start a new thread.

How EnclosingReader produces its output

outer.new Reader associates the reader with outer. Changing outer.label afterward changes the state that reader.read reaches through EnclosingReader.this. The helper does not store a separate label snapshot. The explicit qualified this makes the enclosing receiver visible, although an unshadowed label could also be accessed directly inside Reader.

How AnonymousGreeting produces its output

The anonymous class implements Greeting at the new expression and provides its public text method. person is effectively final, so the implementation can capture its value. The call returns the greeting with Nadia appended. Reassigning person later in the same scope would violate the capture rule and prevent compilation.

Choose a named type when the helper grows

An anonymous class is useful for a short local implementation, but it can become difficult to read when it accumulates several fields and methods. A named nested class gives that behavior a clear identity and makes it easier to test directly. If the helper needs no instance state from its enclosing object, make it static rather than preserving an unnecessary outer reference. For practice, create two Garden objects with different names and one Sign for each. Call both signs after changing only one garden's state. Explain why each helper reads its own associated outer object, not whichever garden variable was most recently assigned.

Java Summary

  • A non-static inner class is associated with an enclosing object and can access its instance state. Creating one outside the outer class uses outer.new Inner().
  • A static nested class has no implicit enclosing instance. It is often preferable when the nested type does not need access to outer object state.
  • An anonymous class defines and creates an unnamed implementation in one expression. It can extend a class or implement an interface.
  • Captured local variables must be final or effectively final. A lambda is usually simpler for a functional interface, but an anonymous class has its own this and can declare additional members.
  • Continue with Java exercises, Java quizzes and the Java study roadmap.

Java Examples

Each example shows the code, the output it produces and what each part does.

Example 1

Java Inner and Anonymous Classes in a complete program

Java
public class GardenInnerClasses  {
    static class Garden  {
        final String name;
        Garden(String name) {
            this.name=name;
        }
        class Sign  {
            String text() {
                return "Welcome to "+name;
            }
        }
    }
    public static void main(String[] args)  {
        Garden garden = new Garden("Fern House");
        Garden.Sign sign = garden.new Sign();
        Runnable announcement = new Runnable()  {
            public void run()  {
                System.out.println(sign.text());
            }
        };
        announcement.run();
    }
}
Output
Welcome to Fern House
Line-by-line explanation
  1. Line 18Evaluates sign.text() and prints Welcome to Fern House.
Example 2

An inner object reads its enclosing state

Java
public class EnclosingReader  {
    private String label = "North";
    class Reader  {
        String read()  {
            return EnclosingReader.this.label;
        }
    }
    public static void main(String[] args)  {
        EnclosingReader outer = new EnclosingReader();
        EnclosingReader.Reader reader = outer.new Reader();
        outer.label = "South";
        System.out.println(reader.read());
    }
}
Output
South
Line-by-line explanation
  1. Line 12Evaluates reader.read() and prints South.
Example 3

An anonymous implementation captures a local

Java
public class AnonymousGreeting  {
    interface Greeting  {
        String text();
    }
    public static void main(String[] args)  {
        String person = "Nadia";
        Greeting greeting = new Greeting()  {
            public String text()  {
                return "Hello " + person;
            }
        };
        System.out.println(greeting.text());
    }
}
Output
Hello Nadia
Line-by-line explanation
  1. Line 12Evaluates greeting.text() and prints Hello Nadia.

Java FAQ

How does this differ in a lambda?

An anonymous class has its own this referring to the anonymous object. A lambda uses this from its enclosing context. This distinction matters when behavior reads instance members or passes a receiver to another method.

Should every helper be an inner class?

No. A helper that does not need an enclosing instance can be a static nested class or an independent class. That avoids unnecessary coupling and prevents the helper from retaining an outer object solely through its implicit enclosing reference.

When should I use a static nested class?

When the nested type belongs with the outer class but doesn't need a particular outer object, such as a Builder, a small result type or Map.Entry. A static nested class is created with new Outer.Nested() and doesn't keep a hidden reference to an outer instance.

Can an anonymous class have a constructor?

No, because it has no name to give a constructor. You can pass arguments to the superclass constructor, as in new Thread("worker") { ... }, and put setup code in an instance initialiser block, { ... }, inside the anonymous class body.

Can inner classes cause memory leaks?

They can. Every inner class object keeps a reference to its enclosing object, so a long-lived inner object, such as a listener stored in a global list, also keeps the outer object alive. If the nested class doesn't need outer state, make it static to avoid that hidden reference.

Java Exercises

Answer each one and check it instantly. If you get stuck, open the hint before the answer.

Multiple choiceeasy

How is a member inner Reader constructed for outer?

Multiple choicemedium

Which variables may an anonymous class capture?

Java Quiz

2 questions to check what you learned.

  1. What does Outer.this identify?

    • The enclosing Outer instance
    • The anonymous class's constructor
    • A static class variable named Outer
  2. Why prefer a static nested helper when no outer state is needed?

    • It avoids an unnecessary enclosing-instance association
    • It automatically makes all helper methods synchronized
    • It permits arbitrary local captures