🪆 Enums, Records & Nested Types · Intermediate

Local classes in Java

Classes declared inside a method.

🧩 The mysteryClasses usually live at the top of a file. But you can declare one right in the middle of a method — and it can read that method's variables. With one strict condition.

A class that lives in a method

A local class is declared inside a method body. Like a local variable, it's visible only inside that block — other methods can't use it — and it takes no access modifier like public or private.

void report(List<Integer> xs) {
    class Stats {
        int max() { return Collections.max(xs); }
    }
    System.out.println(new Stats().max());
}

Capturing locals

A local class can read the method's local variables and parameters — but only if they're final or effectively final, meaning never reassigned after they get their value. The same rule applies to lambdas.

🔮 Predict it

Borrowing a local

What does this print?

void main() {
    String sign = "!";
    class Shout {
        String up(String s) {
            return s.toUpperCase() + sign;
        }
    }
    System.out.println(new Shout().up("hey"));
}
  1. HEY!
  2. hey!
  3. Compile error: can't access sign
Show the answer

sign is never reassigned, so it's effectively final and the local class may capture it.

⚠️ The trap

Modifying a captured local

count++ changes a captured local — and that's forbidden. Local classes (and lambdas) may only use effectively final locals, so this is a compile error. Neither a lambda nor a local class can modify captured locals.

int count = 0;
class Clicker {
    void click() { count++; }   // error
}
🔮 Predict it

Local records

Since Java 16 you can declare a record inside a method. What happens?

void main() {
    int bonus = 5;
    record Score(int pts) {
        int total() { return pts + bonus; }
    }
    System.out.println(new Score(1).total());
}
  1. 6
  2. 1
  3. Compile error
Show the answer

Local records, enums and interfaces (Java 16+) are implicitly static, so they can't capture the method's locals at all — not even effectively final ones.

🤔 Think first

Local class or lambda?

When is a local class a better fit than a lambda?

Think about it, then reveal the answer

When you need several methods or your own fields. A lambda implements exactly one method and can't declare fields. (Neither one can modify captured locals.)

💼 In the real world

Tiny helpers, tight scope

Developers often declare a local record as a temporary tuple inside a method — say, record NameScore(String name, int score) while processing results — so the helper type doesn't leak into the rest of the codebase.

Key takeaways

  1. Visible only inside the enclosing block
  2. Captures effectively final local variables
  3. No access modifiers like public or private
  4. Local records/enums/interfaces (Java 16+) can't use the method's locals
🤯 Did you know?

Local classes compile to files like Main$1Shout.class. The number lets two methods each declare their own Shout without the files clashing.

Practice questions

What does this print?

void main() {
    String greeting = "Hi";
    class Greeter {
        String to(String n) { return greeting + " " + n; }
    }
    var g = new Greeter();
    System.out.println(g.to("Sam"));
}
  1. Hi Sam
  2. null Sam
  3. Compile error: can't access greeting
  4. Hi
Check your answer

Hi Sam. greeting is never reassigned, so it's effectively final and the local class may capture it.

What does this print?

void main() {
    int count = 0;
    class Clicker {
        void click() { count++; }
    }
    new Clicker().click();
    System.out.println(count);
}
  1. 1
  2. 0
  3. Compile error
Check your answer

Compile error. count++ modifies a captured local. Local classes (like lambdas) may only use effectively final locals.

Next: a class with no name at all, declared and used in a single expression.