Skip to content

Lesson 07 · Nested & Inner Classes

Objectives

After this lesson you will be able to:

  • Distinguish static nested, inner (non-static), local, and anonymous classes.
  • Explain how each captures the enclosing instance and local variables.
  • Use qualified this (Outer.this) and reason about shadowing.
  • Know which nested types are implicitly static, and use the right nesting for the job.

The four kinds

KindDeclaredNeeds an outer instance?Can access outer instance members?
Static nestedas a static memberNoOnly static ones
Inner (non-static)as an instance memberYesYes, including private
Localinside a method(lives in that method)Yes (effectively final locals)
Anonymousinline expressionYes (effectively final locals)

Static nested vs inner

A static nested class is just a top-level class scoped inside another — no link to an outer object. An inner class holds an implicit reference to an enclosing instance, so it can read its private members.

java
class Outer {
    private int x = 10;
    static class Nested { int sum(int a, int b) { return a + b; } }   // no Outer needed
    class Inner { int readX() { return x; } }                        // uses Outer.this.x
}

Outer.Nested n = new Outer.Nested();          // no Outer instance
Outer.Inner i = new Outer().new Inner();      // needs an Outer instance

Exam trap

Creating an inner class needs an enclosing instance: the outer.new Inner() syntax. A static nested class is built with just new Outer.Nested(). Since Java 16, an inner class may declare static members (fields and methods) — the old "constants only" restriction is gone.

Qualified this and shadowing

When an inner class declares a member with the same name as an outer one, the inner name shadows it. Reach the outer instance explicitly with Outer.this.

java
class Outer {
    int v = 1;
    class Inner {
        int v = 2;
        int inner() { return v; }            // 2 — the inner field
        int outer() { return Outer.this.v; } // 1 — qualified this reaches the outer instance
    }
}

Local and anonymous classes

A local class is declared inside a method; an anonymous class is a local class with no name, declared and instantiated in one expression — to implement an interface or extend a class on the spot.

java
Runnable r = new Runnable() {        // anonymous class implementing an interface
    @Override public void run() { System.out.println("hi"); }
};

Thread t = new Thread() {            // anonymous class EXTENDING a class
    { setName("worker"); }           // instance initializer — an anonymous class has no constructor
    @Override public void run() { }
};

interface Greeter { String greet(); }
Greeter g = () -> "hello";           // a lambda — lighter than an anonymous class

Gotcha

Local and anonymous classes may only capture local variables that are final or effectively final (never reassigned). Mutating a captured local — or trying to — is a compile error. They can freely read and mutate fields of the enclosing instance. An anonymous class can't declare a constructor; use an instance initializer block for setup.

Implicitly static nested types

A nested interface, enum, or record is implicitly static — it holds no reference to an enclosing instance, whether or not you write static.

java
class Container {
    interface Listener { }    // implicitly static
    enum State { ON, OFF }    // implicitly static
    record Entry(int k) { }   // implicitly static
}

Choosing a nesting

  • Static nested — a helper type tied to the outer class but not to an instance (most common).
  • Inner — when instances genuinely belong to one outer instance and need its state.
  • Local — a one-method helper, named for readability.
  • Anonymous / lambda — a throwaway implementation; prefer a lambda for a functional interface, an anonymous class when you must extend a class or implement several methods.

Beyond the exam

A lambda has no this of its ownthis inside a lambda refers to the enclosing instance, unlike an anonymous class, whose this is the anonymous object. Lambdas (Module 06) replace most anonymous classes for functional interfaces.

Key Takeaways

  • Static nested: no outer instance (new Outer.Nested()); sees only static outer members.
  • Inner (non-static): bound to an enclosing instance (outer.new Inner()), sees its private members; since Java 16 it may declare static members.
  • Use Outer.this to reach a shadowed enclosing member from an inner class.
  • Local/anonymous classes capture only effectively final locals; they may mutate enclosing fields. An anonymous class extends a class or implements an interface inline and uses an instance initializer (no constructor).
  • Nested interfaces/enums/records are implicitly static. Prefer a lambda over an anonymous class for a functional interface.

Lesson Quiz

Lesson Quiz · Nested & Inner Classes0 / 7
  1. How do you create an instance of a non-static inner class Inner of Outer?

    • Anew Outer.Inner()
    • Bnew Inner()
    • Cnew Outer().new Inner()
    • DOuter.new Inner()
  2. Which local variables can an anonymous class capture?

    • AAny
    • BOnly final or effectively final
    • COnly static
    • DNone
  3. A static nested class can access...

    • AAll outer members including private instance fields
    • BOnly static members of the outer class
    • CNothing from the outer class
    • DOnly public members
  4. From an inner class, how do you read a field v of the enclosing Outer instance when the inner class also declares v?

    • Av
    • Bthis.v
    • COuter.this.v
    • Dsuper.v
  5. Since Java 16, an inner (non-static) class may declare...

    • Aonly static final constants
    • Bstatic fields and static methods
    • Cno static members at all
    • Donly an enum
  6. A nested record declared inside a class is...

    • Aimplicitly static
    • Ban inner class
    • Cillegal
    • Dimplicitly final only
  7. Which can an anonymous class do?

    • ADeclare a constructor
    • BExtend a class or implement an interface, with an instance initializer for setup
    • CBe reused by name elsewhere
    • DCapture a non-final local that is later reassigned

Next: Pattern Matching. Run the matching code in labs/src/main/java/com/jse21/m03_oop/.